自作 OS と壁の落書き
三ヶ月前くらいに UEFI 上で動くプログラムをコンパイルしてから楽しくなって自作 OS を作ろうとし始めてた。
自作 OS をどんなふうなものにしたいか。
Linux はファイルであれこれ表現しているからシェルだけでも意外といろいろなことができるけれど、ファイルで表現しきれないものも当然ある。
そういうものはシェルから操作しづらかったり、何らかの仲介役となるバイナリを作らないといけなかったりする。
もっと気軽にいろいろいじれたらいいよね。
ファイルで抽象化することをやめたらどうなるか。
そう考えてみると直接オブジェクトとしてシステムと触れ合えたら良さそうかもしれないと思った。
オブジェクトとして表現するとして、どうやってオブジェクトを取り寄せようか。
適当にカーネル側でグローバルにオブジェクトを保存できる KV ストレージを用意しようかと思った。
あれ、ちょっと待てよ。
いろいろと管理がめんどくさそうだな・・・?
いきなり取り掛かってヤムチャになる前にちょっと調べることにしてみた。
するとマイクロカーネルという種類のカーネルがあるらしいと分かった。
なんだか聞き覚えがあるような言葉、マイクロカーネルについてわかったことを書き上げてみる。
ユーザー空間で実装できるものはユーザー空間で実装する
マイクロカーネルはカーネルをなるべく小さくなるように作る特徴がある。
これは、いらない機能を削ぐことでセキュリティ的に攻撃できそうな部分を減らしたり、システムをある程度粗結合にするための考え方らしい。
カーネルのサイズを大きくしすぎないことについても効果がありそう。
Linux だとかはカーネルモジュールという単位で分割したりしているらしいから、昨今のカーネルでは両者のいいとこ取りのような設計になっていそう。
プロセス間通信 (IPC) でプログラムが連携を取る
厳密に言うとプロセス間通信によくある unix ソケットのような形である必要はなく、関数呼び出しのような形でもいいらしい。
マイクロカーネルで特に面白いのが、ファイルシステムでさえもユーザー空間にあること。
一部のシステムはアロケータもユーザー空間に置くらしくびっくり。
一部のプログラムは特権レベルをカーネルと同じにして動かしたりと工夫してるっぽい。
最近のものだと Blackberry の QNX なんかがマイクロカーネルに分類されるらしい。
BeOS や Amiga OS、2ch 発祥の Mona OS もマイクロカーネルらしい。
まだまだやんわりとした理解しかないけれど、いくつか気になる点もある。
最初に実行されるであろうプログラムはどうやって読み込まれるのか
どうやってサービスを特定するのか
前者はブートローダーで読み込んでしまえばいいと思うけどなんだかズルしてる気分になる。
実際のマイクロカーネルではどう実装されてるのだろうか。 調べる勇気はないけれど、気になる。
後者も調べるのに苦労しそうだと思ったので想像で補填することにした。
とりあえず最初のうちはカーネルでファイルを読み込めるようにしておくとして、サービスの特定とかは最初に実行されたプログラムに取り仕切らせることにした。
最初に実行されたプログラム・・・。 pid を 0 から始めるのは値の初期化をし忘れた拍子とかにあらぬバグを生んでしまわないか不安だったので 1 から始めることにしようと思った。
そう妄想を膨らませているとマイクロカーネルを真似してみればいい感じに作れそうな気配がしてきた。
OSの歯車となるいろんなプログラムが通信で連携する。
Smalltalk のメッセージパッシングと似てる! オブジェクト指向だ!(こじつけ)
なんだかオブジェクト指向へのカルト心が刺激されてきた。
Amiga OS の映像を見たりとワクワク感を更に刺激しつつコードを書いていくのはとっても楽しい。
おわりに
x86_64 向けにカーネルを書いている間に覚えた知ったかぶりのウンチクを書いていく。
INT (ソフトウェア割り込み) の実行中は他の割り込みがブロックされるらしい
int $0x80
をシステムコールに使おうと思い試しにタイマー割り込みを利用したカウントを取得する API を生やしたところタイマー割り込みがブロックされてしまいスケジューラーやらが動かなくなりびっくりした。何も考えずともアトミック性が担保される・・・けど工夫が必要そうだ。
最近だと MSR に
syscall
用のハンドラーのアドレスを書き込み、syscall
命令で呼び出すのが主流っぽい。ただ、
iret
が読み込むような情報はスタックに積まれないのでユーザー空間にあるプログラム側で渡す必要があるっぽい。MSR の書き込みは
wrmsr
という命令でできるらしいけど、今はいい感じにカーネルの機能をまとめられるまでソフトウェア割り込みでシステムコールを表現しようと思った。(実は今の段階の知識ではCPUの特権レベルについてよく知らず、メモリ保護やらも調べてなかったりする。とんだインチキ開発者だぜ。)
A20 Line
ハリーポッターに出てきそうだけど、列車の路線ではないのだ。
8086 といういにしえの 16 ビット CPU 特有の挙動を再現するためのフラグで、初期状態だと 0 になっている。
どんな挙動をするかというと一部のメモリしか使えず、それを超えたアドレスを使おうとするとゲームの空間のようにループして明後日の方向にあるアドレスを指してしまうらしい。
メモリをたくさん使うために A20 Line を有効化してやる必要があるっぽい。
一部のレジスタは mov で書き込めない
RFLAGS (32 bit だと EFLAGS) は
pushf
、popf
という命令でのみ取得、設定できたり。他にも特権レベルによって書き込めるレジスタが制限されたりするらしい。
iret
を利用して特権レベルを書き換えたりできるらしく、スタックバッファオーバーフローとの合わせ技で攻撃にも使われるっぽい。
じゃ👏