ゼロからのOS入門の作業ログ
day08cのMemoryManagerの実装を開始した.
MemoryManagerはページフレームという単位でメモリを使用中か否かという状態を用いて管理する.
OS起動時にメモリのどの範囲が使用中か,あるいはそうでないのかをMemoryManagerに設定するために,UEFIから受け取ったメモリーマップを基にhogehoegする処理を書いた.
しかし肝心のMemoryManagerの実装はまだ歯抜けとなっているので,次回そこをやる予定.
過去の作業ログを転記.
2024/01/14
このエラーを治す.
ld.lld: error: undefined symbol: pci::ScanAllBus()
>>> referenced by main.cpp:136
>>> main.o:(KernelMain)
ld.lld: error: undefined symbol: pci::ReadVendorId(unsigned char, unsigned char, unsigned char)
>>> referenced by main.cpp:142
>>> main.o:(KernelMain)
ld.lld: error: undefined symbol: pci::ReadClassCode(unsigned char, unsigned char, unsigned char)
>>> referenced by main.cpp:143
>>> main.o:(KernelMain)
make: *** [Makefile:19: kernel.elf] Error 1
2024/01/21
PCIデバイス一覧見れた
がんばってこれを書いている
2024/01/28
main.cppのこのへん書いてる
2024/02/04
割り込み方式でマウスが動くようになった.
176pのまとめ助かる
イベントループを使って割り込み命令を処理するように変更した
iOSもタッチスクリーンへの入力やタイマーなどによって割り込み命令が発生し,各スレッドにあるイベントループがそれを処理しているはずなので,iOSもこんな実装なのかな~と想像しながら書いてた.
2024/02/12
08aまで終わった
IsAvailableはまだ実装していない
stack領域は移動した
返信のテスト
MemoryManagerの実装を終わらせてday09aに入った.
day08cのMemoryManagerの実装は以下の2つを抑えていると,内容を理解できた.
- FrameIDはFrameのメモリ先頭位置を表している(単なる識別子ではなく,識別できる事以上の意味を持つ)
- 実際のメモリ先頭位置を得るにはkBytesPerFrameをかける必要がある
- 配列alloc_map_の各要素には,各フレームの利用状況がビット列を使って表現されている
- 1フレームの利用状況は1ビットで表現される
day09aはsbrkの実装まで行った.sbrkはNewLib(Cのライブラリ)にあるmallocの実装が呼び出す関数であり,mallocによって割り当てられるメモリの先頭アドレスを返却する必要がある(多分).sbrkを実装する事によって,独自の割当て処理を実装できる設計はOpne Closedな設計で良いなと感じた.実装が終わってビルドも通っているが,このあたりはランタイムでホゲホゲする処理なので,day08cの実装も含めてちゃんと動くかは謎である.
次回はレイヤー周りの実装をする予定.インスタンスの生成に失敗してたら今日の実装がミスっているということだろう.
day09aの実装を引き続き進めている.LayerやWindowの実装をざっくりと終わらせて,今は淡々とLayerManagerの各関数を写景している.
写景していて謎に感じるのは各Objectの依存関係だ.直感的にはWindowがLayerを所有しているように思っていたのだが,LayerがWindowを参照するコードが多々ある.ある程度実装を進めていくと,このあたりの関係性もわかってくるのだろうか.これ以外にも描画のメカニズムがいまいち理解できていない気がするので,実装後は改めてコードを見直したい.
次はLayerManager::UpDownの実装からやる予定.
9aを終わらせた.長かった.今日具体的に行ったことは,主にLayerManagerの実装およびLayerを使った描画に置き換える事だ.Layerを使った実装に置き変える際に,graphicsやconsoleにも差分が生じていてダルかった.正直,このあたりの描画処理は実装するだけなので,あまり関心が沸かず身が入らなかった.9bからはパフォーマンスの改善に入る予定.
9bを終わらせた.次はPixelの書き込みのパフォーマンスを上げていく.軽く方針について読んだが,全ピクセルをマウスが動くたびに書き込み処理をする(変数へ代入していく)のは無駄なので,memcpyを使ってメモリ領域を効率よくコピーしてくように変える,という方針と理解している.すると,全ピクセル文のループ処理が一つ消えるので早くなる,という理屈.......違うかもしれない.実装している内に完全理解に到れることを願う.
LocalAPICタイマを使ってパフォーマンスを計測する
LocalAPICタイマはレジスタに値を書き込むことで,カウンタの減少スペードを設定できる.
- ウィンドウ画像をフレームバッファに書き込む処理の高速化
- 書き込むべきピクセル数の削減
を行っていく
fatal error: 'cstdint' file not found
とエラーが出たときは、ライブラリが見つかってない状態。対処法がよくわからなかったが、用意されてるshellスクリプトを実行すればとりあえず解決するらしい。
source $HOME/osbook/devenv/buildenv.sh
https://timtoronto634.hatenablog.com/entry/2022/02/03/095331
知らなかった
9cを進めた.シャドウバッファの実装やシャドウバッファを利用する部分を書籍のとおりに行った.しかし,書籍に記載されていない部分の実装はまだ行えていないので,ビルドを通すためにはまだ実装が必要となる.
memcpyのシンボルが見つかってないような挙動が見えたので,少し不安になっている.
もう9章はええやろ
day09を終わらせた.次はday10aの続きからやる.day10aはコードは書ききっているが,qmenueがクラッシュする不具合があるのでその不具合を修正する必要がある.
day10bまで終わらせた.day10cは最初の方だけ実装している.day10cでやる事は,メインループの回数を表示するWindowのチラツキを改善する事だ.UIが描画される順は,まずWindowの背景をまず表示しその後にWindowが表示される順番となる.はじめにWindowの背景が描画された際にはWindowが表示されていないため,Windowが表示されていない時間が一定存在する.そのため,Windowの表示有無が高速に切り替わるのでチラツキが起こる.再描画するエリアを限定することによって,これを解決しようとしている
day10終わった.day11aのinitializeGraphicsとinitalizeConsoleを実装したところで終わった.次は,引き続き肥大化したmain関数を別の関数に切り分けていく作業を行う.
ウインドウにマウスカーソルを重ねるとカーソルにチラツキが起こるのを直していく.
チラつく原因は,ウインドを表示したあとにカーソルを表示するため,カーソルが表示していない期間が存在するから.
ちらつきを回避するためにウインドウ->マウスという複数の描画を行うのではなく,最初に表示する画像のバイナリをバックバッファという場所にためてから,それを画面に一気に表示するという方法を取る.
カーソルを重ねてもチラツキが起きない事を確認した
マウスのクリック時における割り込み処理をハンドルできるようにする
本家リポジトリのosbook_day10fのタグ打ちミスってますな
動いた
リファクタリングする
11dまで進んだ.複数個のタイマーを作成する話だった.ただ,ここまでではタイマーのカウントアップの周期が秒単位ではなくLocal APICタイマの周波数に依存するため,タイマーの経過秒数を測る事ができない.11eではこの問題を解決し経過秒数を計測できるタイマに改善する.Local APICタイマの周波数がわかれば,カウントアップする毎に何秒経過したかが計算できるので,ACPI PMタイマを使ってLocal APICタイマの周波数を計測する.ACPI PMタイマを制御するためにはレジスタやメモリ空間をhogehogeする必要があり大変そう.
day12eまで
12章入った.キー入力の章でタイマーの続きの理由をやる理由がよくわからない
Local APICの周波数をAPIC PMを使って測定してLocal APICのタイマーを作るんじゃなくて,すでに周波数がわかってるAPIC PMだけを使ってタイマーを作るのじゃアカンのか?
APIC PMに割り込みの仕様がないのかも
うーん.定義しているのにシンボルが見つからない.ヘッダーもインポートしている.なぜ?
次調べる.
ld.lld: error: undefined symbol: DrawTextbox(PixelWriter&, Vector2D<int>, Vector2D<int>)
>>> referenced by main.cpp:89
>>> main.o:(InitializeTextWindow())
make: *** [Makefile:26: kernel.elf] Error 1
なおった
day12fから
RIP
次に実行する命令のアドレスを示す
RSP
スタックの先頭のアドレスを示す.
スタックはスタックフレームという単位で,ローカル変数,RBP,リターンアドレスのの順で情報を保存している.
RBPはスタックの底のアドレスを示す.リターンアドレスは関数の終了後にどこにジャンプするかを示す.
ノイマンコンピュータはレジスタから命令を1つずつ取り出して実行していくだけなので,並行処理(コンテキストスイッチ)を実現するためには,レジスタの値を書き換えて次に実効するタスクを変えてCPUを騙す.
タスクを切り替えるだけなら,切り替えたい先のタスクの先頭アドレスをRIPに書き換えることにより実行先を変更し,タスクの引数をRDIとRSIにいれるだけで良いが,切り替え元に戻るためには元のタスクの状態も保持しておき適切に状態を復元する必要がある.
C++の非同期処理機能を使えないの?とふと思ったが,言語機能がOSのラインタムAPIに依存しているからありえないかww
次は13bから