##はじめに
Tech Commit の LT発表会 で OS のコードリ-ディングをやってらっしゃる方のお話を聞いて、自分もまた UNIX V6 のコード読みたい欲が高まり、積ん読してた書籍 をパラパラめくってみたのですがやはり難しい。
記載されている古い書き方の C言語やアセンブリを解読しつつ OS の仕組みを理解するのは自分の場合茨の道だなぁとあらためて感じました。
エミュレータ で実際にコードを動かしながら読めば分かりやすいのかな?と思い色々調べているうちに、xv6 というマサチューセッツ工科大(MIT) の Operating System Engineering のクラスで使用される UNIX V6 を ANSI の C で書き換えて x86 (またはRISC-V) ベースに作りなおされた OS がある!という事を知ったので、早速動かしてみようと思い立ちました。
xv6 について
UNIX V6 がベースであるため OS の基本的なアイディアがほぼ全て詰まっているけどコード量は1万行弱(Linuxの場合2200万行以上)で、OS の学習にはもってこいだと思います。
xv6 が起動する仮想環境の準備
xv6 は仮想マシン上で動かすことが想定されており、bochs や QEMU で動くように環境が整備されています。以前は x86 の仮想環境で動作しましたが、最近の版では RISC-V に変わっているためクロスコンパイル用の開発ツールをインストールする必要があります。Linux の場合は比較的簡単です。
今回は WSL2 でやりましたが、Ubuntu20.04 でも全く同じ手順で大丈夫だと思います。
まずは必要なパッケージをインストールします。
$ sudo apt-get install qemu-kvm git build-essential gdb-multiarch qemu-system-misc gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu texinfo
次に xv6 のソースコードを取得しビルドします。
$ git clone git://github.com/mit-pdos/xv6-riscv.git
$ cd xv6-riscv
$ make
ここまで出来たら QEMU で xv6 を起動してみます。 同ディレクトリ上で make qemu
と打ちます。
$ make qemu
qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
xv6 kernel is booting
hart 1 starting
hart 2 starting
init: starting sh
$
上記の様に起動し、ターミナル上で xv6 のシェルが起動し入力を受け付ける様になります。
xv6 から抜けるには [ctrl-A][x]の順にタイプします。
GDB のインストール
GDB(GNUデバッガ)は、プログラムの不具合解析や値を参照・一時的に変更する等して動作をテストする際に使用するツールです。ここでは RISC-V 用にクロスデバッグ可能な gdb をインストールします。
こちらからソースコードをダウンロードし、以下の手順でインストールします。
$ tar xvzf gdb-10.1.tar.gz
$ cd gdb-10.1
$ ./configure --program-prefix=riscv- --target=riscv
$ make
$ sudo make install
(※上記でパスが通ったディレクトリにインストールされなかった為、 sudo cp gdb/gdb /usr/local/bin
でバイナリを配置しました。ここ後で修正します。)
Vim によるステップ実行
Vim8.1 から、標準で GDB を扱える用になっています。これがものすごく便利です。
まずは xv6 を gdb モードで起動します。
$ make qemu-gdb
*** Now run 'gdb' in another window.
qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 -S -gdb tcp::26000
もう一つ別なターミナルを起動後 xv6-riscv
ディレクトリに移動し vim を起動、コマンドモードで :Termdebug kernel/kernel
とタイプすると、下図の様な画面になります。
左上の gdb のウィンドウで下記の様にタイプします。
(gdb) target remote localhost:26000
(gdb) cd kernel/
(gdb) directory .
(gdb) b main
(gdb) c
すると、main.c
の 13行目にブレークポイントが設定され、画面が下図の様になります。
あとは右ウィンドウの任意の行でマウスを右クリックすることによりブレークポイントを設置したり、変数のマウスオーバーによる値の表示、上部メニュー ( Step Next Finish Cont Stop Eval
) のクリックでステップ実行などの操作が自由に実施できます!
おわりに
vim の :Termdebug
は初めて使いましたが、こんなに便利だとは思いませんでした笑
これで OS コードリーディングがめっちゃ捗りますね!
興味があったら是非試してみてくださーい、ではでは!