raku1chi

Web バックエンドエンジニア

個人開発では公的データやオープンデータ、好きなものを題材に「調べる・比べる・眺める」が気持ちよくできる Web サービスを作っています。

読書約5分で読めます

『作って理解するOS』を読む第1回

Mac で OS を作り始める

『作って理解するOS』を読み始めました。初回は Mac に開発環境を構築し、「何もしない」ブートプログラムを QEMU と Bochs で起動するところまで進めました。

読んだ範囲:P.16「本書に掲載しているコードについて」、付録 A.1(QEMU)、第 12 章、14.1

『【改訂新版】作って理解するOS x86系コンピュータを動かす理論と実装』(林高勲 著、川合秀実 監修、技術評論社)を読み始めました。仕組みを理解しておけば、安心して効率よく開発できると考えたからです。AI ですぐに開発できるこの時代に、裏側で何が起きているのかをおおまかに理解しておくことは重要だと思っています。Web 開発でも、経験を積むにつれて自分のしていることの見当がつくようになり、そのぶん速く開発できるようになりました。学習も AI の支援を受ければ効率よく進められると考えています。

進め方

本書は第 1・2 部で理論を、第 3・4 部で OS の実装を扱います。頭から読むと、手を動かすまでに 11 章ぶんの理論が続きます。そこで初回だけ順番を入れ替え、第 3 部の冒頭にある開発環境の構築(第 12 章)と「何もしない」ブートプログラムの起動(14.1)を先に済ませました。先に動く環境があれば読み続けやすく、第 1・2 部の内容も手元で試しながら読めます。本書は Windows 向けなので、Mac で動かすときの問題を最初に片付けておきたいという理由もありました。次回からはイントロダクションに戻り、本の順に読み進めます。

動かしたもの

Mac で本書と同じ手順を再現できるように、リポジトリ make-os を用意しました。本書のサンプルコードに、Windows 用のバッチファイルの代わりとなるシェルスクリプトと、Mac 用の Bochs の設定を加えたものです。必要なツールは Homebrew で入ります。

brew install nasm qemu bochs
git clone https://github.com/raku1chi/make-os ~/make-os
echo 'export PATH="$HOME/make-os/env:$PATH"' >> ~/.zshrc

QEMU が x86 の CPU をエミュレートするので、Apple シリコンの Mac でも追加の設定なしで動きます。

QEMU で起動する

ターミナルを開き直し、14.1 のサンプルのディレクトリで boot.s をアセンブルして QEMU で起動します。

cd ~/make-os/src/00_boot_only
mk.sh && boot.sh

SeaBIOS と iPXE の行に続いて「Booting from Hard Disk…」と表示され、そこで止まります。14.1 のプログラムは無限ループするだけで何も表示しないので、画面に出ているのは BIOS の出力です。

QEMU のウィンドウ。SeaBIOS と iPXE の表示のあとに「Booting from Hard Disk…」と出て止まっている
何もしないブートプログラムを Mac の QEMU で起動した

Bochs のデバッガで確かめる

第 12 章では QEMU に加えて Bochs でも動作を確認します。Bochs にはデバッガが組み込まれていて、実行を止めてレジスタやメモリの中身を見られます。

mk.sh && box.sh

起動ログの最後に Booting from 0000:7c00 と出たところで Ctrl+C を押してデバッガに入り、次のコマンドを順に実行しました。

  • r:レジスタを表示する
  • x /16xb 0x7c00:メモリの 0x7C00 から 16 バイトを表示する
  • s:1 命令だけ実行する
  • q:Bochs を終了する

出力は次のとおりです。r の結果は一部を省略しています。

Next at t=38758762202
(0) [0x000000007c00] 0000:7c00 (unk. ctxt): jmp .-2  (0x00007c00)     ; ebfe
<bochs:1> r
rax: 00000000_8000aa55
rbx: 00000000_00000000
rcx: 00000000_00090000
rdx: 00000000_00000080
...
rip: 00000000_00007c00
eflags: 0x00000282: id vip vif ac vm rf nt IOPL=0 of df IF tf SF zf af pf cf
<bochs:2> x /16xb 0x7c00
[bochs]:
0x0000000000007c00 <bogus+       0>:    0xeb    0xfe    0x00    0x00    0x00    0x00    0x00    0x00
0x0000000000007c08 <bogus+       8>:    0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
<bochs:3> s
Next at t=38758762203
(0) [0x000000007c00] 0000:7c00 (unk. ctxt): jmp .-2  (0x00007c00)     ; ebfe
<bochs:4> q

わかったこと

  • mk.sh は NASM で boot.s をアセンブルし、512 バイトのディスクイメージ boot.img を作る
  • QEMU と Bochs はどちらも x86 の PC をエミュレートし、boot.img をハードディスクとして起動する。Bochs はデバッガで実行を止めて、中身を確認できる
  • Bochs で止めると、実行位置(rip)は 0x7C00 で、メモリの 0x7C00 には boot.lst にある jmp $ の機械語(eb fe)が入っている。s で 1 命令進めても 0x7C00 から動かず、無限ループしている

ほかのレジスタの値が何を表しているのかは、第 2 部で電源投入から最初の命令が動くまでを読むときに改めて確かめます。

つまずいたこと

Mac で動かす際に引っかかったのは次の点です。調査と修正には AI の手を借り、修正したものは make-os に入れています。

バッチファイル

本書の手順ではサンプルに付属するバッチファイル(mk.bat・boot.bat・box.bat)でアセンブルと起動を行いますが、Mac では使えません。make-os には同じ処理をするシェルスクリプト(mk.sh・boot.sh・box.sh)を用意しました。

コメントの文字コード

サンプルのコメントは Shift_JIS で書かれているため、Mac のエディタではそのままだと文字化けします。make-os では UTF-8 に変換しました。その際、Shift_JIS として読み込むと行継続の \ が ¥ に化けてアセンブルできなくなるので、Windows 版の Shift_JIS である CP932 として読み込んでいます。

QEMU のウィンドウサイズ

Retina ディスプレイでは、QEMU のウィンドウがかなり小さく開きます。メニューの Zoom To Fit を有効にすると、ウィンドウを広げたときに画面も合わせて拡大されます。make-os の boot.sh は Mac ではこの状態で起動するので、開いたあとはウィンドウを広げるだけです。

Bochs の設定ファイル

サンプルに付属する Bochs の設定(bochsrc.bxrc・cmd.init)は Windows 向けで、Homebrew の Bochs 3.1 ではそのままでは起動しません。Windows 専用の表示方式や BIOS のパス、Homebrew 版に含まれないプラグインが指定されていて、cmd.init も改行コードが CRLF のままだと読み込めません。make-os には Mac 用に修正した設定(bochsrc-mac.bxrc・cmd-mac.init)を置き、box.sh から読み込むようにしました。変更点は make-os の README にまとめています。

Bochs の PANIC

Bochs の起動時に >>PANIC<< ata0-0: specified geometry doesn't fit on disk image と表示されますが、動作には影響しません。設定ファイルで指定したディスクの大きさ(320KB)より boot.img が小さいために出るもので、panic: action=report の設定により報告だけで処理は続きます。

次に読むところ

イントロダクション、第 1・2 章