ゼロからのOS自作入門 | day04

前回はブートローダとカーネルを切り離して画面への描画を行いました。ようやくOSが形になった記念すべき回でした。

そして今回はmakeとピクセル描画を行います。
前回も画面に対してフレームバッファの情報から変な描画処理を行いましたので、その応用ですかね

そしてmakeというのは、今までのコマンドをまとめたシェルのようなものらしいです、前回、今後使いそうなコマンドとかいってまとめましたが使わなくなりそうですね。(記事の内容はやらせではありません)

make

Makefileを指示書としてmakeコマンドを利用することで、カーネルのコンパイルとリンクなどをまとめます。

利用する際にはmakeとするだけでMakefileの内容が実施されます。

day03までエラー起きなかったのにエラー起きるようになった。しかも、これ頻出らしいです。zshだと出るってあったけどsh bashと試してエラー出ました。まぁこういうのは仕方ないですね

#include <stdint.h>

で回避できるそうです。

ピクセルを描く

この辺はゲーム開発で画面に描画する理論と似たようなものですね。

さてここでもまたビルドが失敗しました。さっきと同じ理由です。

githubのwikiに書いてあるとおり実行してもだめでした。

コードを写経していたんですが、ブランチをday04bに変えてwikiに書いてあるコマンドを再度実施

source $HOME/osbook/devenv/buildenv.sh

これでmakeコマンドが使えましたが、せっかくコード書いてたのに残念です。

今回のコードにはC++特有の参照型が使われています。参照型の引数を持つ関数をC言語から呼び出すにはポインタを指定するとのことです。

実行すると以下のようになります

クラス化

ピクセル描画に関してクラス化します。

C++のメモ

C++は、前に触ったんですがほとんどわからずやっていたので、完全に初心者です。

アクセス修飾子ごとに関数やメンバを定義します。

~(チルダ)から始まる関数はデストラクタらしいです。つまりインスタンスが消えたときの処理でしょうか。Javaには無いですよね?

virtualメソッドは仮想関数です。つまりオーバーライド可能なメソッドということでしょうが、C#のとは微妙に挙動が異なるらしいです。

継承クラス(RGBResv8BitPerColorPixelWrite)で、仮想関数をオーバーライドします。

using 親クラス::親クラスとすることで親クラスのコンストラクタをそのまま利用できます。

クラスの最後の}にセミコロン;をつける

C++はホイスティングのようなものはない?void* operator newを利用箇所より後ろで定義したらエラーになった。そもそもここクラス構文の中じゃないのでそういうものか・・・?少し勉強不足です。。。

無駄処理の削減

今までは描画する際に毎度WritePixel関数を呼び出していました。しかしその中でif文を利用していたので描画するたびに条件分岐が入っていた

何で描画するか予めswitchで決めておいて無駄なifでの条件分岐をなくします。これはポリモーフィズムの良さが出ていますね。

実行

vtableの説明

ここでの記載は省略します

ローダの改良

現状はカーネルの読み込み時のメモリ確保にバグがある。

プログラムヘッダの確認

misaka@misaka:~/workspace/mikanos/kernel$ readelf -l kernel.elf

Elf ファイルタイプは EXEC (実行可能ファイル) です
エントリポイント 0x101080
There are 5 program headers, starting at offset 64

プログラムヘッダ:
  タイプ        オフセット          仮想Addr           物理Addr
                 ファイルサイズ        メモリサイズ         フラグ 整列
  PHDR           0x0000000000000040 0x0000000000100040 0x0000000000100040
                 0x0000000000000118 0x0000000000000118  R      0x8
  LOAD           0x0000000000000000 0x0000000000100000 0x0000000000100000
                 0x0000000000000180 0x0000000000000180  R      0x1000
  LOAD           0x0000000000001000 0x0000000000101000 0x0000000000101000
                 0x00000000000001c9 0x00000000000001c9  R E    0x1000
  LOAD           0x0000000000002000 0x0000000000102000 0x0000000000102000
                 0x0000000000000000 0x0000000000000018  RW     0x1000
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x0

 セグメントマッピングへのセクション:
  セグメントセクション...
   00     
   01     .rodata 
   02     .text 
   03     .bss 
   04     

LOADセグメントによってファイルのデータをメモリにコピーする(loadする)

3つめのLOADセグメントだけ.bssセクションを含み、ファイルとメモリのサイズが異なっている。

.bssセクションには初期値なしのグローバル変数が配置される
→pixel_writer_bufやpixel_writerなど。

初期値がないのでファイルに記録する必要がないのでファイルの大きさが0ということ。

ELFはファイルヘッダとプログラムヘッダとセクション本体とセクションヘッダからなる。

e_phoff

e_phoff

e_phoff : ELFファイルヘッダのプログラムヘッダファイルオフセット→プログラムヘッダ(配列)を取得。

AllocatePool() AllocatePages()メモリ領域を確保する関数だが、AllocatePoolはページ単位でなくバイト単位なのでカーネルファイルを一時領域に読み込む。

開始アドレスと終了アドレスをkernel_first_addr、kernel_last_addrにそれぞれ格納する。→メモリの大きさを図れる。

今回はコードの話多め。

ブートローダ側の処理は頭が追いつかない。。。

その他

エディタをvscodeに変えたらとても快適です。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする