[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
SlideShare a Scribd company logo
C++でできる!OS自作入門
OS入門へのいざない
C++勉強会in広島
@uchan_nos
そもそもOSとは

•

身近な例 Windows, OS X, Linux, iOS, Android

•

ハードウェアとアプリの橋渡しをするソフト

•

メモリ管理、ハードウェア制御、タスク管理、…
OS自作 #とは

•

Windowsみたいなソフトを自分で作る

•

具体的にどうやって作るのか?
!

•

そんなあなたに『OS自作入門』
  30日でできる!OS自作入門

30日!

マルチタスク

GUI
OS自作の楽しさ
•

自分のコンピュータを支配している感覚

→ ハードウェアを直接いじる

•

普段使ってるOSの裏側を実体験できる

→ メモリ管理、タスク管理ってどうなってる?

•

独自アイデアを盛り込んだOSの創造

→ エミュレータOS、クラウドOS
OS自作の苦労
•

自分がOSなので、OSの助けを借りられない

→ main関数さえ自分で呼び出す

•

画面表示も自分の責任

→ 画面表示にバグがあると涙目

•

エミュレータと実機での差

→ エミュレータだと上手く動いたのに!
BITNOS
•

私が作っているOS

•

NOS = Not Operating System

OSぽい画面のジョークソフト

(Akkie氏命名)

•

Bit NOS : ちょびっとだけNOS

•

5回くらい作り直しているが、
まだ全く完成しない
OS起動プロセス
OSってどうやって起動するの??
OSの起動 MBR読み込み

MBR: Master Boot Record

BIOS
先頭セクタ
= MBR

0x7c00
0x7e00
OSの起動 MBRコピー
0x7a00
0x7c00
0x7e00

MBR
MBR

自身をコピー&
コピー先へジャンプ
OSの起動 パーティション解析

パーティション情報
OSの起動 PBL読み込み

PBL: Partition Boot Loader

MBR

パーティション
先頭セクタ
= PBL

0x7c00
0x7e00

MBR
PBL
OSの起動 なぜMBRをコピーするか
BIOSを模倣するため
•

パーティションがないメディア

→ PBLがディスク先頭

•

BIOSは機械的にディスク先頭を0x7c00に読み込む

•

MBRがPBLを0x7c00に配置し、BIOSを模倣
OSの起動 PBLの仕事

•

FATを解析

•

"BITNOS.SYS"を探してロード
•

•

ローダー+カーネル(2段階ロード)

ロード先へジャンプ
OSの起動 ローダーの仕事
•

BIOSを使う用事を済ます

→ 画面モード設定、キーボードLED状態取得

•

プロテクトモード(32ビットモード)へ移行

•

カーネル部分を、所定の位置へコピー

→ リンカへの指示と合わせる

•

カーネル(kmain)にジャンプ
カーネルの仕事
アプリ アプリ
•

ハードウェア初期化

カーネル
ハードウェア

•

割り込み管理

→ キーボード/マウス、タイマ、ネットワーク

•

タスク管理

•

メモリ管理
OS開発とC++
OS自作入門とC言語
•

『30日でできる!OS自作入門』はC & アセンブリ

•

C言語でキューの実装→だるい
int以外のキュー
struct FIFO32 {
int *buf;
int p, q, size, free, flags;
};

!

も欲しいのだが…

void fifo32_init(struct FIFO32 *fifo, int size, int *buf)
{
fifo->size = size;
fifo->buf = buf;
fifo->free = size;
fifo->flags = 0;
fifo->p = 0;
fifo->q = 0;
}
C++でOS開発
•

先人たち
•

BayOS : C++コンパイラだけで対応可能な機能だけ使用

•

MonaOS : 例外、純粋仮想関数なども使用

•

「gccをg++に置き換えるだけで使える機能」は簡単

•

でも、C++ならそれらしい機能使いたいよね!
•

純粋仮想関数、例外、実行時型情報、new/delete
OS開発におけるC++の楽しさ
•

『30日でできる!OS自作入門』のC言語コードを
改良していく楽しさ
•

•

キューをクラスで実装し直すのが好き

OS開発がオブジェクト指向言語でできる楽しさ
•

継承を使って分かりやすいコード

•

純粋仮想関数ひゃっはー
OS開発におけるC++の苦労
•

『30日でできる!OS自作入門』の開発環境を流用できない
•
•

•

自分でクロスコンパイラを用意
特に川合さん謹製リンカは.ctorsや.rodataに対応しない

C++特有の機能への対応
•

純粋仮想関数、例外、実行時型情報、new/delete、グ
ローバル変数コンストラクタなど
C++特有の機能への対応

•

new

•

純粋仮想関数

•

グローバル変数コンストラクタ
C++特有の機能への対応

•

new

•

純粋仮想関数

•

グローバル変数コンストラクタ
new演算子

•

new演算子は

1)メモリを確保し、2)コンストラクタを呼ぶ

•

OSが空きメモリを探し、メモリを割り当てる

•

g++がコンストラクタ呼び出しコードを追加する
new演算子の実装
void* operator new(size_t size)
{
void* buf = my_malloc(size);
return buf;
}
void* operator new[](size_t size)
{
void* buf = my_malloc(size);
return buf;
}

カーネルの.cppに書いておく
メモリ割り当て - リンクリスト方式
0x1000

Header* current
= (Header*)0x1000;

Allocated

0x2000

size=0xFF8
true

Header* next
= (Header*)(
(uintptr_t)current
+ sizeof(Header)
+ current->size);

size=0xAF8
false

Released

0x2B00

struct Header
{
size_t size;
bool is_allocated;
};
配置new演算子
•

new演算子は

1)メモリを確保し、2)そこでコンストラクタを呼ぶ

•

配置new演算子は

1)指定されたメモリ領域でコンストラクタを呼ぶ

•

プログラマがメモリ領域を与える

•

g++がコンストラクタ呼び出しコードを追加する
配置new演算子の実装
普通のnew

配置new

void* operator new(size_t size)
{
void* buf = my_malloc(size);
return buf;
}
void* operator new(size_t size, void* buf)
{
return buf;
}
配置newの使いドコロ
例えばこんな場面
ScreenRenderer* screen_renderer;
!

void KernelMain(void) {
BIOSで設定した画面解像度など取得;
!

!

}

if (24ビット色モード) {
screen_renderer = new 24ビットレンダラ(解像度);
} else if (32ビット色モード) {
screen_renderer = new 32ビットレンダラ(解像度);
}
…
配置newの使いドコロ
例えばこんな場面
ScreenRenderer* screen_renderer;
!

様々なところで使う
→グローバル変数

void KernelMain(void) {
BIOSで設定した画面解像度など取得;
!

!

}

if (24ビット色モード) {
screen_renderer = new 24ビットレンダラ(解像度);
} else if (32ビット色モード) {
screen_renderer = new 32ビットレンダラ(解像度);
}
…
配置newの使いドコロ
例えばこんな場面
ScreenRenderer* screen_renderer;
!

様々なところで使う
→グローバル変数

void KernelMain(void) {
BIOSで設定した画面解像度など取得;
!

!

}

if (24ビット色モード) {
screen_renderer = new 24ビットレンダラ(解像度);
} else if (32ビット色モード) {
screen_renderer = new 32ビットレンダラ(解像度);
}
…

でもインスタンスは
後で生成したい
→ newを使う
配置newの使いドコロ

•

普通、newは新たなメモリを割り当てる

(コンパイラがコンストラクタ呼び出しコードを自動で追加)

•

メモリ割り当てはOSの仕事

•

割り当て機能が無い段階でnewを使いたい!

•

→ 配置new
配置newの使いドコロ
class A {
int val_;
const char* str_;
public:
A() : val_(41), str_("foo") {}
};

41

確保した
メモリ

41
"foo"

"foo"

new A();

char buf[32];
new(buf) A();

buf
配置newの使いドコロ
配置newで無事解決!
ScreenRenderer* screen_renderer;
char buf[128];
void KernelMain(void) {
BIOSで設定した画面解像度など取得;
!

!

}

if (24ビット色モード) {
screen_renderer = new(buf) 24ビットレンダラ(解像度);
} else if (32ビット色モード) {
screen_renderer = new(buf) 32ビットレンダラ(解像度);
}
…
C++特有の機能への対応

•

new

•

純粋仮想関数

•

グローバル変数コンストラクタ
仮想関数テーブル

•

C++で仮想関数を使うと出てくる用語

•

virtualなメンバ関数のアドレスの表

•

クラス毎に生成され、インスタンスはvtableへのポインタを持つ

class A {
public:
virtual void foo();
virtual void bar();
};

A::foo
A::bar
class A {
public:
virtual void foo();
virtual void bar();
};

class B : public A {
public:
virtual void foo();
};

A's vtable

B's vtable

A::foo
A::bar

B::foo
A::bar
class A {
public:
virtual void foo();
virtual void bar();
};

class B : public A {
public:
virtual void foo();
};

A's vtable

B's vtable

A::foo
A::bar

B::foo
A::bar
B instance
vptr

p1

A* p1 = new B();
class A {
public:
virtual void foo();
virtual void bar();
};

class B : public A {
public:
virtual void foo();
};

A's vtable

B's vtable

A::foo
A::bar

B::foo
A::bar
B instance
vptr
A instance
vptr

p1
p2
A* p1 = new B();
A* p2 = new A();
class A {
public:
virtual void foo();
virtual void bar();
};

class B : public A {
public:
virtual void foo();
};

A's vtable

B's vtable

A::foo
A::bar

B::foo
A::bar
B instance
vptr

A instance
Aのポインタ経由でも
vptr

正しくBのメソッドを呼べる

p1
p2
A* p1 = new B();
A* p2 = new A();
仮想関数テーブル実物
class Base
{
public:
virtual ~Base() {}
virtual int foo() = 0;
virtual int bar() {return 42;}
};
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI4Base)
16 (int (*)(...))Base::~Base
24 (int (*)(...))Base::~Base
32 (int (*)(...))__cxa_pure_virtual
40 (int (*)(...))Base::bar

class MyClass : public Base
{
public:
virtual int foo() {return 43;}
};

0
8
16
24
32
40

(int (*)(...))0
(int (*)(...))(& _ZTI7MyClass)
(int (*)(...))MyClass::~MyClass
(int (*)(...))MyClass::~MyClass
(int (*)(...))MyClass::foo
(int (*)(...))Base::bar
仮想関数テーブル実物
class Base
{
public:
virtual ~Base() {}
virtual int foo() = 0;
virtual int bar() {return 42;}
};
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI4Base)
16 (int (*)(...))Base::~Base
24 (int (*)(...))Base::~Base
32 (int (*)(...))__cxa_pure_virtual
40 (int (*)(...))Base::bar

自分で実装

class MyClass : public Base
{
public:
virtual int foo() {return 43;}
};

0
8
16
24
32
40

(int (*)(...))0
(int (*)(...))(& _ZTI7MyClass)
(int (*)(...))MyClass::~MyClass
(int (*)(...))MyClass::~MyClass
(int (*)(...))MyClass::foo
(int (*)(...))Base::bar
C++特有の機能への対応

•

new

•

純粋仮想関数

•

グローバル変数コンストラクタ
コンストラクタ呼び出し

class MyClass {
int val_;
public:
MyClass() : val_(41) {}
};
!

MyClass global_instance;
!

int main(void) {
…
}

コンストラクタは
誰が呼ぶ?
コンストラクタ呼び出し

class MyClass {
int val_;
public:
MyClass() : val_(41) {}
};
!

MyClass global_instance;
!

コンストラクタは
誰が呼ぶ?

int main(void) {
…
}

→規格では、main関数実行前に呼ばれる
コンストラクタ呼び出し

class MyClass {
int val_;
public:
MyClass() : val_(41) {}
};
!

MyClass global_instance;
!

コンストラクタは
誰が呼ぶ?

int main(void) {
…
}

→規格では、main関数実行前に呼ばれる
誰が呼ぶんだろう?
コンストラクタ呼び出し
処理系が用意
コンストラクタ呼び出し
大域変数0初期化

_start

return

PBL

call
main

普通のアプリ

jump
自分でやる

kmain

カーネル
コンストラクタ呼び出し
.ctors
0x000005A8
0x00001277
0x000018ED

コンストラクタを呼び出す
関数のアドレス

void __call_constructors()
{
typedef void (*ctor_caller_t)(void);
extern int __ctors, __ctors_count;

}

ctor_caller_t* ctors = (ctor_caller_t*)&__ctors;
uarch_t ctors_count = (uarch_t)&__ctors_count;
for (uarch_t i = 0; i < ctors_count; ++i)
{
ctors[i]();
}
まとめ

•

OS自作とは

•

OSの起動シーケンス

•

OSとC++
•

new, 純粋仮想関数、コンストラクタ

More Related Content

C++でできる!OS自作入門