ゼロからのOS自作入門備忘録

Mikan OS

2022.06.12

mikanos備忘録

ゼロからのOS自作入門、いわゆるミカン本を4章まで進めたが結局何をやっているかよくわからず作業になっている気がしたので5章から何をやったのかまとめることにする。
といっても動かすのはめんどくさいので7章あたりからどういう流れで作るのかと面白いところだけ読むようになった。


5章 文字表示とコンソールクラス

5.1

まずAという文字を画面に表示させます。Aという文字の描画するピクセルの位置に1が入るような2進数の配列を用意し、これを2重ループで画面に表示します。

5.2

mainファイルが大きくなってきたのでgraphics, font, mainと担当ごとに分ける。分割コンパイルの話やヘッダファイルと本体ファイルの書き方等。

5.3

5.1で作ったAの文字を他の文字分組み込む。既存のものを仕様し、カーネルに組み込むのに変換ツールを使用。組み込んだ文字を画面に表示。

5.4

5.3までは1文字しか表せなかったが、ループで文字列を表せる関数を用意。C言語の%dのような書式にも対応させる。(Cのsprintfを通してループさせるだけ)。

5.5

ディスプレイに文字列をどばどば表示する機能を追加。なるほどと思ったのは、普段プログラムを書く際は画面に出力すればそこで終了だが、OSではディスプレイに表示する文字列はディスプレイから消えるまで記憶しておかねばならないということ。

5.6

コンソールをグローバルに登録してどこからでも参照できるようにすることでprintk関数(カーネル内部からメッセージを出す機能)を追加。


6章 マウス入力とPCI

6.1

マウスカーソルの形をフォントの時と同じように描画。背景やタスクバー用に四角形を塗りつぶす関数も用意。

6.2

マウスを操作できるようにすべくUSBドライバを作っていく。USB機器に搭載されているターゲットドライバに対し、OSに乗せるのはホストドライバ。これも階層状に作っていく。ホストコントローラを制御するホストドライバ、ホストコントローラの詳細を隠してAPIを提供するUSBバスドライバ、USBターゲットごとにはクラスドライバが必要。章を通して実装していく。

6.3

PCIデバイス(部品とマザーボードを繋ぐ規格)でデバイスを探索。256バイトのPCIコンフィグレーション空間を順に読み、ベンダIDやクラスコードを調べていく。 PCIデバイスはメモリアドレスとは別の周辺機器用:IOアドレス空間に繋がっている。

6.4

xHCという規格でマウスを探し、接続する。マウスの移動はポーリングで検知。


7章 割り込みとFIFO

7.1

マウスの処理をポーリングで実装したが無限ループ内でOSのあらゆる処理を実装するのは至難の業で、効率も悪い。そこで割り込みを使う。

準備

  • 割り込みハンドラ
  • 割り込みハンドラをIDT(割り込み記述しテーブル)に登録 イベント発生時
  • ハードウェアがイベントをCPUに通知。
  • CPUは処理を中断し、割り込みハンドラに処理を移す。

7.2

割り込みハンドラを登録。割り込み終了時に呼ばれる関数はメモリではなくレジスタに値を書き込むことでCPUに割り込み終了を通知する。

7.3

7.4

割り込み要因番号と割り込みハンドラを対応付けるテーブル、IDT(割り込み記述子テーブル)の定義や記述子の設定等。

7.5

MSIという割り込み方式では割り込み終了時と同様にメモリバスへの書き込み動作で割り込み開始の合図とする。

7.6

まとめ

  • 割り込みには割り込みはハンドラの設定が必要
  • 割り込み記述子は割り込みハンドラのアドレスを設定

7.7

割り込み処理に時間がかかる。 割り込みが来たことを記録しておくことにする。

7.8

FIFOのデータ構造の説明

7.9

キューの実装説明。

7.10

キューを使って割り込みを高速化。


8章 メモリ管理

8.1

8.2

メインメモリの状況を把握するためにブートローダからメモリマップをカーネルに渡す。メモリマップを順番に表示するように変更。

8.3

メモリ領域が空き領域かどうかはメモリマップのtypeから判断する。 UEFIが起動時にブートローダー専用データ領域で作成したデータをOSの管理するメモリ領域へと移動する。

8.4

スタック領域を移動する。スタック領域の末尾を指すレジスタをメモリ領域の末尾に指定する。

8.5

CPUの動作権限を決めるセグメンテーションの設定に必要なデータ構造GDTを再構築する。

8.6

ページングの設定。といっても物理アドレスとリニアアドレス(論理アドレスがセグメンテーションによって変換されたもの)が等しくなるアイデンティティマッピングの最低限の設定を行う。

8.7

メモリ領域を4KiBページフレーム単位で管理するメモリマネージャを作る。


9章 重ね合わせ処理

9.1

9.2

これまで指定したメモリ領域にインスタンスを構築するだけの機能だったのをスタック領域を広げてそこでメモリを確保できるようにする。(Cでいうmalloc())
そのためsbrk()という関数を実装する。この関数はprogram breakと呼ばれる書くプロセスが使えるメモリ領域の末尾を指すポインタを後ろにずらしてメモリを確保する。

9.3

画面を原点座標と重なり順のみを属性として持つレイヤの重ね合わせと考える。 細かい実装はとばした。

9.4

9.5

マウスを動かす処理が遅いため高速化を行う。

  • ウィンドウ(各レイヤ)をフレームバッファに書き込む処理の高速化
  • 書き込むピクセル数の削減(マウス周辺のみを書き換える)

9.6

9.7

スクロールを高速化


10 ウィンドウ

10.1

マウスが画面の外に出ると中心から点対称な位置からマウスが現れるのを修正

10.2

ウィンドウを作成。レイヤはもちろんマウスより下。

10.3

メイン関数のループ回数を数えてウィンドウ内に表示

10.4

カウンタが増えるたびに画面全体を書き換えているため画面がチラつく。ウィンドウだけ書き換えるように変更。

10.5

マウスをウィンドウに重ねるとチラつく。マウスはウィンドウよりも上のレイヤのため、ウィンドウの描画中はマウスが描画されていないのが原因。フレームバッファ(書き込むと画面に描画される画面と1対1のバッファ)に書き込むのではなく、フレームバッファと同じ大きさをもつバックバッファに書き込んでからフレームバッファに転送する仕組みに変更。

10.6

ウィンドウのドラッグ機能を実装。
まずマウスのボタンの押下状態が渡されるように変更する必要がある。マウスドライバがマウスからデータを取得したときに呼ばれる関数を変更。 また、ビットごとにマウスのボタンの押下状況が分かるように変更。 次に必要なのはある座標地点上にウィンドウを持つレイヤを表示順に探す関数。

10.7

ウィンドウごとにドラッグ可能かの属性をもたせる。

11 タイマとACPI

11.1

ソースコードの整理

11.2

コンソールに入力機能をつける。入力カーソルの点滅にタイマを使う。 Local APICを短い時間で周期動作させる。時間がくるたびに割り込みを発生させる。

11.3

11.2の方法では数秒の周期ごとの時間しかはかれなかった。これをミリ秒まで小さくする。

11.4

設定した時間が経過すると通知するタイムアウト通知を実装する。複数のタイマーの中からタイムアウトしたタイマを探す必要があるため優先度付きキューでタイマーを管理。タイムアウトが小さい順に並び替える。

11.5

これまでのタイマはCPUの動作に影響を受けるため1カウントが何秒かわからなかった。これを動作周波数がわかっているタイマを使って秒であらわせるようにする。
ACPI PMタイマというタイマはコンピュータの構成や電源を管理する規格。これを利用する。


12章 キー入力

12.1

12.2

Local APICタイマの1カウントの秒数を計測。

12.3

OSはマウスの時と同様キーボードドライバとやり取りをする。
まず、キー押下イベントを処理するハンドラをUSBドライバに登録する関数を作成。 このハンドラはキーが押されるたびに引数にkeycodeを渡され呼び出される。これをASCIIコードに変換するint->charの関数。

12.4

モディファイアキー(Shiftなど)が機能するように変更。12.3のイベントハンドラに引数を追加してモディファイアキーの状態も受け取れるようにする。

12.5

テキストボックス内に文字が打てるように。

12.6

テキストボックス内に点滅するカーソルを表示。


次の章くらいから面白そう。勉強になりそう。


13章 マルチタスク(1)

13.1

複数のアプリが同時に起動するとき、それぞれのアプリではスタックメモリ、環境変数、レジスタの値などバラバラ。これらのことをコンテキストという。マルチタスクでは各処理が短時間でコンテキストを切り替える必要がある。 2通りのマルチタスク実現法

  • 複数のCPUコアで動かす並列処理
  • 時分割の並行処理

今回は並行処理のみ行う

13.2

2つのコンテキストを切り替えるコンテキストスイッチの実装。次の命令を指すRIPレジスタやスタックメモリの末尾を指すRSPレジスタを別のコンテキストのものにすることで切り替えることができる。後で戻ってこられるように切り替え前のコンテキストのRIP, RSPの値を保存しておく必要がある。

13.3

13.2で実装したコンテキストスイッチは互いにSwitchContext()という関数を呼び合っている(協調的マルチタスク)。これをCPUの割り込み処理で強制的にタスクを切り替えるpプリエンプティブマルチタスクにする。タイマ割り込みが発生するたびにタスクを切り替える。

13.4

切り替え時間を1秒にしてプリエンプティブマルチタスクが実装できていることを確認。

13.5

登録するとマルチタスクを実現してくれるTask classを実装する。 ここでTaskが増えるとマウスが引っかかることを発見。


随時更新中 途中で更新が途切れていたら察してください。