WSL2で「30日でできる!OS自作入門」に取り組む
環境
WSL2
Ubuntu 22.04.4 LTS
CDドライブはないので出来るだけ手元やオンラインにあるものだけで頑張る
あんま関係ないけど
VSCode
Zsh
参考サイト
これの中のリポジトリも参考にさせていただきました
低レイヤに関する筆者のレベル
電子工作を趣味にしてて、PIC(マイコン)のアセンブリでフルスクラッチで色々(SDカードライブラリなど)書いたことがある。
CやC++の低レイヤの言語も割と出来て、C++は競プロ用のデバッグツールを作った経験がある。
ので、アセンブリが何かとか、コンパイルされたときにどんな感じにアセンブリになってメモリはどう使われるかとか、Makefileが何かとか、そういう基礎的なことはもともと知ってた。
(あと論理回路の仕組みは回路レベルで知ってるけど、それは今回関係ないと思う?)
ただ、所詮PICのアセンブリで、パソコンのCPUの命令セットは触ったことがないのと、言語は知っててもOSの仕組みはほとんど知らないって感じです!
それでも、僕が分からなかったところを書いていく感じなので、説明が端折られていたらごめんなさいmm
リポジトリ
submoduleは29日目までインストールしなくても使える。
1日目
sec. 1
大分前にやったのでこの投稿日時は全然違うのと、アプリのインストールなどで端折ってる手順があるかもしれない…。バイナリエディタのリンクは切れてた?気がするので窓の荘のリンクを使う。他にもいろいろ試してみたがこれが一番使いやすかった。
プロセッサエミュレータQEMU(キューエミュ(ム))をインストール。
sudo apt install qemu-kvm
この中のqemu-system-i386
を使ってエミュレートする。
qemu-system-i386 -drive format=raw,if=floppy,file=helloos.img
普通にコマンドにファイル名を渡しても動くが、本と同じ環境を確実に再現するためにこの指定の仕方をする。まず、i386はiはインテルで、386は初めて32ビットを扱えるようになったCPU(インテルx86系列)(参考)。format
は指定しないとワーニングが出る。if
(インターフェース)はディスクイメージを入れたことにするドライブ。この時点まででCPUの指定はなかったかもなのでなぜインテルx86系列なのかという話があるが、後々本が32ビットの話をしているのでこれでいいんじゃないかと。-cpu
でCPUの型番が指定でき、公式ドキュメントによれば指定しなかった場合x86系列の全ての拡張が使えるようになるみたいだが、それは脆弱性があるのでやめた方がいいとのこと。でもこの本では恐らくどんなx86系でも動く命令しか使わないかなと思うのと、あまり本と違うことをして動かなかったら困るので、CPUの指定は一旦しないでおく。エミュレータはときどき失敗したように見えるが、ウィンドウの拡大とかリフレッシュさせると直ると思う。
とはいえqemu-system-i386
じゃなくて他のCPUのエミュレータでも動くみたい。その互換性に関しては後で調べたい。
ディスクへ書き込みをしましょう、みたいなのがあるが、これはやってない。
sec.3
CDが読めずnask
が使えないのでnasm
を使う。
sudo apt install nasm
NASMとはNetwide Assemblerの略でインテルx86を対象としたアセンブラ(Wiki)。アセンブリにディレクティブを加えることでCPUタイプを指定できる(公式ドキュメント)。こちらのCPUタイプも一旦指定しないでおく。デフォルトでは全ての拡張命令が使える。リポジトリのファイルとしてはhelloos2.nasm
で、以下でコンパイル、実行。RESB
命令はワーニングが出るのでTIMES
に置き換えてあるのと、他にも変えてるとこがあるがそれは参考文献を参照。
nasm helloos2.nasm -o helloos2.img
qemu-system-i386 -drive format=raw,if=floppy,file=helloos2.img
なお、VSCodeの拡張機能としてはNASM Language Supportと、CoolSpy3's Assembly Formatterを使っている。
レジスタの初期化にわざわざAX
を使っているのは、レジスタによっては定数初期化の命令がないからみたいだ。試しにAX
を0
とかにして保存すると、拡張機能に怒られると思う。
2日目
sec. 2
nasm helloos3.nasm -o helloos3.img
qemu-system-i386 -drive format=raw,if=floppy,file=helloos3.img
CD読み取れんのでブートセクタ以降のコードがどうアセンブリになってるかが分からん。と思ったけど、逆アセンブルしてもアセンブリにならんのでそもそも命令じゃないっぽい。まあ後でわかると信じよう。
ORG
疑似命令を若干勘違いしていたのだが、この疑似命令により0x7c00
に配置されるのではなく、0x7c00
に配置されるのは決まっているからそれをアセンブラに教えるための命令だとわかった。このおかげで$
による相対位置で指定ができる。
sec. 4
make helloos4
何やってるかはMakefile
参照。dd
コマンドはブロック単位でファイルをコピーするコマンド(参照)で、一つ目のdd
はただのコピー、二つ目はファイルサイズを1474560にしている。なお、空いた部分は0フィルされる(スパースファイル)。これを参考にstrace
してlseek
関数について調べればなぜ0フィルされるかわかると思う(参考)。ただ、lseek
関数ではファイルサイズが変更できないとのことだが、変更できてしまっている(stat
やdiff
を使って調べた)。これはバグなのか?もしバグだったら以下のように置き換えるといい。
# 2つ目のddはこうも書ける
dd if=/dev/zero of=$@ bs=1 count=1 seek=1474559
3日目
sec. 1-4
cd harib00*
make run
4つのフォルダはMakefile共通である。
sec. 5
sudo apt install mtools
cd harib00e
make run
Makefileが変更有、haribote.nasmが新規追加。
Makefileのmformat
はMS-DOSフォーマット(参考1、参考2)を行うものらしい。オプションは-f 1440
がファイルシステムのサイズを"1440K, double-sided, 18 sectors per track, 80 cylinders"(man mformat
参照) にする、-B
がブートセクタの指定、-C
がMS-DOSファイルシステムの載ったディスクイメージファイルを作る、-i $@
がフォーマットするファイルである。mcopy
の方は最後に::
がついているが、これはディスクイメージ内MS-DOSファイルシステム内のルートのパスだと思う。理由はman mtools
上で::ファイル名
みたいな例があるのと、mdir
やmmd
を使って調べてみたから。他のサイトだとmformat
の方でも::
をつけているが、mformat
の方はなくても動くので省略可能と判断した。mcopy
の方で省略するとバグる。ここら辺の説明がマニュアルで乏しいのはなぜなのか😭。意味としては、Linuxのファイルシステムと、フロッピーディスクのファイルシステム(MS-DOS)が違うから、mtoolsを使って読み書きしないといけないということだろう。
sec. 6
cd harib00f
make run
ipl.nasmとharibote.nasmに変更有。
sec. 7
cd harib00g
make run
haribote.nasmに変更有。真っ黒な画面が出てくると思う。なんか最初調子悪かったが、ALの他のモードを試したりしていたら直った。
sec. 8
cd harib00h
make run
haribote.nasmに変更有。
sec. 9
cd harib00i
make run
ipl.nasm以外は変更有。
gccのコマンドはこの記事の中のリポジトリを参考にした。-nostdlib
はシステムライブラリのリンクをしない、-m32
は32ビット環境用のコードを生成する、-fno-pie
はPIE(Position Independent Executable)にしないというオプションである。-T
はリンカスクリプトの指定で、このページを参考にしているみたい。このスクリプトの仕様は今度調べることにしよう。
sec. 10
cd harib00j
make run
nasmfunc.nasm追加、Makefile、bootpack.cに変更有。nasm
の-f elf32
はLinux向けi386のコードを生成するオプションである。どう違うのかわからないが、Windows向けにしても動いた。MacOS向けは動かない。ここに関しても後で調べたい。
4日目
sec. 1
cd harib01a
make run
bootpack.cとnasmfunc.nasmに変更有。
sec. 6
cd harib01f
make run
bootpack.cとnasmfunc.nasmに変更有。
sec. 7-8
cd harib01*
make run
bootpack.cに変更有。
5日目
sec. 4
以降コマンドは省略する。いつも通りmake run
でよい。また、今回からフォーマッタclang-format
を導入したのと、僕好みの書き方にちょいちょい変えてある。あと、unsigned char
とchar
が混在していたが、文字('a'
等)や文字列(const char *
)を表す場合を除きunsigned char
に統一した。なお、clang-format
はVSCodeの拡張機能があるのと、ルートの.clang-formatというファイルが設定ファイルである。ちなみにランゲージサーバーはclangd
が僕のおすすめ(VSCodeの拡張機能にあるので入れてみよう)。
(ちょいちょい思うのだが時代のせい(そもそも言語自体やmake等が高機能でなかったか、ノウハウが蓄積されてなかった)なのか何なのかわからないが、可読性や書き方などこの本のコードの書き方には思うことがある。ただ、いちいち修正してたら時間がかかるので気になったところだけ変えることにする。)
sec. 5
hankakuフォルダ追加、bootpack.c、Makefileに変更有。
本ではhankakuの配列のオブジェクトをつくってリンクしているが、ここではhankaku.cというCファイルを作ってリンクさせている。
sec. 7
libフォルダ追加、bootpack.c、Makefileに変更有。
lib/my_spintf.cはこちらを参考にさせてもらった。
sec. 9
bootpack.c、nasmfunc.nasmに変更有。
なお、勘違いしてしまうがこの時点では画面上の変化はない。
6日目
sec. 3
ただソースファイルを分割しただけ。cフォルダの中身とMakefileのみ変更有。
sec. 6
bootpack.c、bootpack.h、dsctbl.c、int.c、nasmfunc.nasm、Makefileに変更有。
学んだこと
今度からコミットログ貼るようにしようかな。
C言語自体は元から知ってるし説明することがなくなってきた。だったら学んだことを書こうかな。
割り込みはPIC(Programable interrupt Controller)という別チップで監視されてる。割り込みがCPUに通知されるとIN,OUTの信号線でどの割り込みかを通知する。PICはマスタとスレーブの二つを使って15個の割り込みを監視する。それぞれの割り込みで呼び出される関数の番地はIDT(Interrupt Descriptor table)に登録しておく。IDTはメモリに配置し先頭アドレスを専用のレジスタに登録しておけばよい。そしてIDTの設定のためにはセグメントの設定が必要。これはなぜかというとIDTの一つ一つのデータは関数の番地(正確にはオフセット、32bit)、セグメント番号(16bit)、割り込みを表すトークン?(16bit)で構成されているから(8バイト)。セグメント番号もGDT(Global (Segment) Descriptor Table)にIDTと同じように登録する。GDTの一つのデータはセグメントの大きさ(20bit)、先頭アドレス(32bit)、セグメントの属性(書き込み・実行禁止、システム専用など、12bit)で構成され、8バイト。セグメントの数は8192個で、GDTのメモリ上のサイズは8*8192=64kB。セグメントの大きさを表すビットは20ビットしかないので、1MB以上の場合は属性のGフラグを立て、ページの数として解釈させる。その場合×4kBのサイズになる。よく言うSegmentation Faultとか、ページとかってこれのこと言ってるんだろうな。なるほどねぇ。
IDTの一つ一つのデータはGATEとプログラム上ではなっていたが、これは後で説明があるのか。それともただの割り込みのGATEという意味だろうか。GATEの構造体の詳しい説明がなかったが後で説明があるのか読み落としたのか。
メモリ容量の確認は今回は書き込みを行って本当に覚えているかでチェックした。その際にキャッシュがバッファにならないようにオフにした。キャッシュの管理ってOSがやってるんじゃなくてハードウェア由来なのか。
割り込み時のレジスタの内容はスタックに記録する。そのとき、PUSHADといった命令があるが、これのクロック数を調べたい。あと、これは全てのレジスタを記録できているのか?
7日目
sec. 1
コミットログ
ちなみにこの後編集していたりするのでフォルダのその後のログを確認すると良い。
なお、これのついでにmy_sprintf.cをリッチにしておいた。
sec. 5
コミットログ
今後言わないが、今後のコミットもこの後編集している可能性がある。
sec. 7
学んだこと
マウスもキーボードも複数バイトのデータを送ってくるのでFIFOにためて処理する。なお、それぞれのbyteで毎回割り込みが発生する。マウスは3byteそれぞれで押しているボタンの情報、x、yの移動量を送る。押しているボタンに関しては押したときと離したときに割り込みを発生させているようだ。送るデータには特に離したという情報はつけず、今押しているボタンの情報を送る(割り込みの発生した回数を表示させて調べた)。キーボードは2byteでキーを押しているか離したかの情報とキーコードを送る(p142)。押している間ずっと割り込みが発生するみたい(割り込みの発生した回数を表示させて調べた)。確かに何も押してないのに押したままと勘違いするみたいな誤動作を考えるとこれがいいかもだ。と思ったら、キーによって違うようだ。Ctrlキーは押したときと離したときにしか反応しない。キーコードは確かにCtrlは2byteだが、他のキーは同じByteを重ねるときもある。そうか、これで長押ししてるということを示してるのかな。また、長押しフェーズに入るまで割り込みは発生しないから、長押しの時間とかもハードウェア側で処理しているっぽい。
マウスやキーボードで1byteずつ割り込みが発生しているのは最新のCPUでもそうなのか?
長押しの時間とかキーのリピート速度って確かOS側で設定可能だった気がするがそれはBIOSの設定か何かか?
確か普通はメモリはリンクリストで管理してるんじゃなかったっけ。
でもその場合順番に並べて管理するみたいなのは難しいな。そこはどうしてるんだろ。
free[]の4000個で十分らしいが本当だろうか。最近のOSはどうしてるんだろう。
11日目
sec. 2
コミットログ
Window外サポートって些細だけど興味深いな。シートで管理してるからWindow外を無限遠まで考えなくて良いという。
sec. 3
sec. 4
sec. 6
sec. 7
sec. 8
コミットログ
変更部分についてmapを更新してから書き込む。mapの計算量は変わらないが、mapを作り替えなくて良い場面(各シートのスライドがなく、不透明が透明になったりしていないとき。一枚のシートにバックグラウンドを書いてそのあと文字を書いたりしている場合など)はシート一枚分の書き込み量になる。mapを作り替える場面でも追加の書き込み量は書き換えなくて良いシート(変更があったシートより上のシート)が分かるので少ない。
学んだこと
やったこととしてはWindowを表示させてみたのと、mapなどを使い一回当たりの画面更新量を減らすかつレイヤが重なっている部分の再描画回数を減らしちらつきを抑える工夫。基本的に前回のグラフィックの続き。
計算量解析的に速度が怪しい。実際のPCもこうなっているのか確認。
13日目
sec. 2
sec. 3
harib10c
のコミットログ harib10d
のコミットログ
一つ目はフォルダ名がharib10f
となっているが間違い。
恐らくエミュレータのせいだと思うが、カウントのたびに画面更新をしないと実行速度が遅くなるという問題が発生した。毎ループで割り込みの禁止・許可をしているから、その周期があまりに短すぎることが問題ではないかと疑い、空forループを入れることで実行速度を回復させることができた。
sec. 4
コミットログ バグ修正
本ほど早くならなかった。まあforループで1万も回しているから当たり前である。
sec. 5
sec. 6
学んだこと
今までのアルゴリズムの改善を行った。マウスやキーボード、タイマのFIFOを一つに統一した。その際、それぞれのコードをシフトすることでそれぞれのデータを表していて、そのためにFIFOを1データ4byteに拡張した。タイマはリンクリストに変更し、ずらし処理をなくした。(あと番兵を使ってコードを短くした。)最近リファクタリングばかりだな。
STIやCLI命令はCPU上で割り込みを禁止して、PICは監視を続けるイメージ。割り込みがスタックした場合、STI命令が実行された瞬間に割り込みされるものと予想しているが違うのか。でなければQEMUが割り込み禁止・許可の周期でPITのカウントが遅くなる理由(QEMU上のPITはクロック数のカウントで動いてるという前提だが)が分からない。
15日目
sec. 2
sec. 4
sec. 5
コミットログ
例の問題が発生するので空for文を追加している。
sec. 6
sec. 7
学んだこと
JMP命令で飛んだ先のセグメントが実行可能セグメントではなくTSS(Task Status Segment)だった場合、CPUは全レジスタを今行っているタスクのTSSに書き込む。また、JMP先のTSSセグメントが保存しているレジスタの情報を全てロードし、(拡張)命令ポインタ(EIP)もその保存された値になる。TR(Task Register)というレジスタがあり、今行っているTSSを覚えるレジスタだそうだ。これはタスクスイッチング時に自動的に変更されるようで、マルチタスクの初期化時にだけ明示的にセットしていた。スタックも新しく用意してESPに入れる。自動で定期的なタスクスイッチングをするためには、定期的に割り込んで、割り込みハンドラの中でJMP命令を行う(次タスクスイッチングをして戻ってくる場所は割り込みハンドラ内のその次の命令である)。なお、ここで使うJMP命令はfar-JMPといって、CS(コードセグメントレジスタ)とEIPの両方に書き込む命令である。それと、今更気づいたがこのOSやC上ではアドレスが32bitで表される。なんかおかしいとずっと思っていたがそういうことか。
セグメントレジスタはコード用(CS)とスタック用(SS)とデータ用(DS)があるらしい(他にもあるがおまけらしい)。p134の記述によると「C言語では『DS(データセグメント)もES(エクストラセグメント)もSS(スタックセグメント)も同じセグメントを指している』という思い込み」があって、だからSSをDSとESに代入するそうだ。セグメントが違うデータにはアクセスしない前提なのか。
また、もしそうならSS=DS=ESが保たれているC言語の途中で呼び出された割り込み処理内でわざわざ代入する必要はないと思うがどうなのか。まあこれに関してはタスクスイッチング中などは異なる可能性あると思う。
と思ったが、どうやらセグメントの意味を完全に誤解していた。セグメントは4GBにアクセスするための手段だと思っていたが、32bitモードになった今セグメントはその手段ではなく、プログラムのアドレスを0からにリセットして使えるようにするためのものらしい。なんじゃそりゃ。だったらDS=ES=SSも納得できるな。(もちろんCSは一致していない。)
今回の場合、CS=2<<3、DS=ES=SS=1<<3で、どちらのタスク(マルチタスクの)もこれらのセグメントを使っている。1<<3は4GB全体を示すセグメント、2<<3はbootpack.sysがあるセグメントである。ここら辺の整理をしなければ。読み飛ばした8日目の32bitモードへの道の記述の中にヒントがあると思われる。
タスクスイッチング中のJMP命令によるレジスタの保存のクロック数が知りたい。
TSSの登録の際、limitを103としていたが、これはなぜなのか。104byteあるのにどういう意味があるのか。
ちなみにJMP命令の正体はEIPへのMOV命令らしい。ここら辺も整理したり、使えるレジスタについても学びなおしたい。
ディスクはBIOSを使わないと読めないからメモリに読んでおいてやっていたがさすがに実際のOSでは毎回ディスクから読んでいるはず。じゃあその瞬間は16ビットモードに切り替えているのか?
割り込みルーチンを使わないAPIの作り方はどんなものがあるか知りたい。アセンブリからC言語の関数を呼び出す前にレジスタの保存を行っていたが、C言語でもこれを毎回やっているのか?呼び出された関数側でやらないのか?
21日目
sec. 1
sec. 2
sec. 3
sec. 4
sec. 5
sec. 7
学んだこと
アプリ用のセグメントを作り、そこに飛ぶことで、自動的にOS用のセグメントにアクセス不可にし、スタックなどの切り替えをする。この際、現在のタスクのTSSにESP(スタックポインタ)とSS(スタックセグメント)を手動で保存する(これはタスクスイッチで上書きされたり読み出しされたりしない別のプロパティとして保存される)。アプリ用のセグメントへの飛び方はアプリからOSを呼び出して戻るときのようにスタックを調整し無理やりRETFを使って飛ぶ(OSからアプリを呼び出すことは出来ないようになっている)。TSSへの保存を手動で行っているが、アプリの終了時も終了APIを呼び出して手動で保存していたESPを書き換えて、ここでようやく本来のstart_app()のRETを使って元の呼び出し元へ戻る。この際、TSSに保存していたスタックポインタにスタックを戻す。
- アプリ用のセグメント前にCS(コードセグメント)とEIP(命令ポインタ)をPUSHするのはRETするから理解できるとして、その前にSSとESPをPUSHしているのはなぜだろうか。アプリからOSを呼び出す(CALL)ときは必ずそれが行われているということだろうか。おそらくそうだろうが、そこの説明がなかった気がする。
- TSSにESPとSSを保存していて、API呼び出しの際にSSの読み出しが明示的に行われていなかったがこれは自動的に行われるということだろうか。TSSの変数の命名をちゃんとすればわかると思う。
- Cで作ったアプリの最初の6バイトを書き換えていて、mainをCALLしているとの説明だったが、正確にはそうではないという含みがあるのはどういう意味なのか。
- Cで作ったアプリのデータ用セグメントを適当に64kBにしていたが、静的データを使い始めるともっときちんと決める必要が出てくるはず。
- アプリのメタ情報がJMP命令になっていてHariMainに飛ぶJMP命令として解釈できると後に説明がある(p461)。
- のちにアプリの先頭(コード領域)にメタ情報を組み込み、そこでデータセグメントのサイズなどを指定できるようにしていた(p460)。
22日目
sec.1
sec.2
sec. 3
sec.4
sec. 5
sec. 6
sec. 7
学んだこと
IN、OUT、CLI、HLTなどの命令はアプリ側から実行すると一般保護例外になる。指定された場所以外のOSのfar-CALLも例外になり、今のところINT 0x40(API、内部でOS側の関数のfar-CALLをする、この割り込みのみアプリ呼び出し許可をしている)でしかOSを呼び出すことは出来ない。例外は他にも除算例外、無効命令例外などがある。アプリの強制終了は他のタスクからTSSのEIPを書き換えて、次にタスクスイッチした時にアプリを終了するAPI(の中身)に飛ぶようにする。今回、C言語アプリ内で静的データを使ったので、そのためのデータ領域への転送をする処理を追加している。
23日目
sec. 1
sec. 2
sec. 3
sec. 4
sec. 6
sec. 8
学んだこと
malloc APIはOS側のmemman_allocを呼び出してメモリの確保を行う。この際、アプリのメタ情報(コード領域)から取得したmalloc用のデータ領域をfreeして初期化する。MEMMAN構造体はアプリ内に用意する(malloc領域の先頭に位置しAPI内で自動で参照される)。その他、点描画、線描画、ウィンドウリフレッシュ、キー入力APIを作った。キー入力APIはキー入力があるまでタスクをスリープさせる。強制終了のウィンドウの閉じ方は、SHEETとタスクを関連付けさせ、終了後にそのタスクに残っているSHEETをすべて開放することで行っている。
- 強制終了時のウィンドウの開放がそれに関連付けられたSHEETの開放なのはOOP的には違和感があるが、これは普通なんだろうか。まあ確かにメモリの開放じゃ終わらなくて、SHEETを再描画しないといけないのは分かるが。
- malloc領域のサイズ=データ領域のサイズ(128kB)-32kB-mallocの開始アドレスとしていたが、普通は必要に応じてアプリ用のセグメントの大きさを変更するものらしい。それはどうやってやるものなのか。
- なお、本ではmalloc領域が40kBとあるが、ldsファイルの中身的にそれ以上が割り当てられている気がする。
26日目
sec. 1
sec. 2
sec. 4
sec. 5
sec. 6
sec. 7
sec. 8
sec. 9
sec. 10
学んだこと
4pxずつウィンドウを動かすことにしてMOV DWORD命令で画面を更新して画面の更新を早くした。コンソールをキーボードからいくらでも開けるようにした(基本的にはメモリを確保してtask_run()、その後はmain()からタスクのfifoにpushしてデータを送る。確保したメモリやタスクの情報はSHEETに保存することでアクセスする)。startコマンドやncstコマンドを作った。ncstコマンドはただコンソールの表示をしないでアプリを実行しているだけ。
27日目
sec. 1
sec. 2
sec. 3
sec. 4
sec. 5
sec. 6
sec. 7
コミットログ
そこまでコンパイル時間はかかっていないため、フォルダ構成は変えず軽い整理や即時終了アプリの修正などを行った。
学んだこと
それぞれのタスクからしか参照できないローカルなセグメント(LDT(Local (Segment) Descriptor Table、GDTみたいな))をコード領域やデータ領域に用いることでその他のアプリからのアクセスをシャットアウトできる。LDTはタスクを切り替えるときにLDTRレジスタに保存する(スリープ中はTSS内)。LDTには8192個分(64kB)までテーブルを設定できる。LDTのセグメントの指定の仕方はLDTのセグメント番号×8+4とすることで指定できる。LDTRはシステム以外から書き換えることは出来ないため、これで他のタスクのローカルセグメントへのアクセスが不可能になる。LDTのセグメント番号はタスク間で被っても問題ない。LDTのセグメントはGDTにLDT用のセグメントとしてTSSのように登録しておく必要がある。この際にサイズを指定することで何個分のテーブルかが指定できる。
29日目
sec. 1
sec. 2
git submodule update --init --recursive
make run
コミットログ
TEK形式に変換するためにHariboteOS/z_tools_linuxを使った。WindowsならHariboteOS/z_tools_winを使えばいいと思う。
sec. 3
sec. 4
sec. 6
学んだこと
標準関数を作った。他はアプリづくりなど。mallocは確保した領域の先頭にサイズを保存していた。サイズ情報はどっか別に保存しているんだと思っていたが、いや確かにこれでいいな。
他のOSのmallocもこんな感じなのか。
読み終えて
低レイヤについて学びたいと思い立ち、低レイヤーを学ぶための技術書をまとめてみるを参考にこの本に手を出した。最初やる気がわかず買ってから3日目に到達するまでに数か月かかってしまったが、その後は爆速で開発し1か月程度で終わらせることができた。何なら、後半は数日分/1日程度のスピードで進めていた。そしてこの分量で、OSは何をしているのか、APIはどう実装されているのか、OS作りは意外と難しくないんだということを知ることができた。30日でも、たった50kBでもOSは動作するということが分かった。率直にこれはすごいと思う(作者が)。
しかし、まだまだOSについて学ぶという観点では物足りないものがある。作者も最後に触れているが、ディスクアクセス、デバイスドライバ、ページング、マルチコア、GPU等々、それらについて触れられていない。また同時に、はりぼてOSではこうだが、普通のOSではどうしているんだろうという疑問があった。前者についてはコンピュータシステムの理論と実装 ―モダンなコンピュータの作り方やみかん本(最近存在を知った)、後者についてははじめてのOSコードリーディング ~UNIX V6で学ぶカーネルのしくみがいいのではないかとみている。いずれにせよ、この本のおかげで他も読み進める速度は速いだろう。その意味ではこの本はさっさと終わらせるべきだったのだが…。
このスクラップにはいくつもの疑問を残してきている。それについてもいずれ調べ、その時にももう一度「勉強し終えて」のようなものを書きたいと思う。