このブログは、株式会社フィックスターズのエンジニアが、あらゆるテーマについて自由に書いているブログです。
Linuxカーネルに新機能を追加したり、デバッグなどの変更をしたら、動作を確認するテストを実行するわけですが、実機で実行しようとするといろいろ面倒です。そこでQEMUの登場です。QEMUはシステムエミュレータの1つで、x86 PCをはじめARM, SPARC, MIPS, PPCなど、いろいろなプロセッサアーキテクチャをサポートした非常に高機能なエミュレータです。また、KVMでデバイスをエミュレーションする用途でも使われています。
QEMUをカーネルの開発に使うことには以下のメリットがあります。
手間がかからずに、ビルドしたカーネルを試せるのは、非常に便利です。
今回は、QEMUでLinuxカーネルを実行する環境を構築するところまでを見ていきたいと思います。
http://www.qemu.org/download/ からバイナリまたはソースコードダウンロードするか、https://github.com/qemu/qemu からcloneします。ソースコードをダウンロード (またはgit clone) した場合はコンパイルする必要がありますが、自分の用途に合わせて configure 可能です。
x86_64のLinuxカーネルだけ実行できれば十分という場合は、例えば以下のように configure を実行します。
% ./configure --target-list=x86_64-softmmu --enable-kvm --enable-vnc --disable-vnc-sasl
softmmuというのが、カーネルを実行可能なプロセッサエミュレータを指定するオプションになります。コンパイルするために必要なライブラリが足りないと configure がエラーになりますので、足りないものを追加するか、必要ないと思ったらエラーを出したオプションをdisableします。上記の例では、コンソールをVNCで表示可能にするオプションはenableするけど、saslでの認証はdisableしています。
configure できたら make するだけです。
% make -j
コンパイルが終了したら x86_64-softmmu/qemu-system-x86_64 が出来ています。
当然ですが、LinuxカーネルをQEMUがサポートするデバイスに合わせて、また必要ないデバイスや機能は外してコンフィグすれば、コンパイルする必要のあるファイルが減りますので、ビルドの時間も短くなります。QEMU がサポートするデバイスはドキュメンテーション http://www.qemu.org/documentation/ に書かれています。また、QEMUでお手軽にLinuxカーネルを実行するには、ローダブルモジュールは使わず、vmlinuxに全部リンクしてしまいましょう。
Linuxカーネルがビルドできたら早速実行してみましょう。
% qemu/x86_64-softmmu/qemu-system-x86_64 -m 256 -nographic -kernel linux-stable/arch/x86_64/boot/bzImage -append console=ttyS0
オプションは -m の後にはメモリサイズ (MB)、-kernel の後にカーネル (bzImage) を指定します。今回は、ターミナルにカーネルからのメッセージが表示されるようにシリアルコンソールを使用します。そのために、QEMUのオプションして -nographic を指定し、Linuxカーネルの方には console=ttyS0 が渡されるように -append の後に付けておきます。
[ 0.000000] Linux version 4.4.76 (shui@blazar) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC) ) #1 SMP Fri Jul 14 19:09:12 JST 2017
[ 0.000000] Command line: console=ttyS0
...
[ 0.781373] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
[ 0.782059] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.4.76 #1
[ 0.782059] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.10.2-0-g5f4c7b1-prebuilt.qemu-project.org 04/01/2014
[ 0.782059] 0000000000000000 ffff88000ecabdf8 ffffffff8120d92f ffffffff814bd790
[ 0.782059] ffffea00003bf780 ffff88000ecabe70 ffffffff810c861f ffff880000000010
[ 0.782059] ffff88000ecabe80 ffff88000ecabe20 61200035316d6172 ffff88000ecabe88
[ 0.782059] Call Trace:
[ 0.782059] [] dump_stack+0x4d/0x6e
[ 0.782059] [] panic+0xbd/0x1d4
[ 0.782059] [] mount_block_root+0x1fd/0x287
[ 0.782059] [] mount_root+0x67/0x6a
[ 0.782059] [] prepare_namespace+0x167/0x19f
[ 0.782059] [] kernel_init_freeable+0x192/0x1a0
[ 0.782059] [] ? initcall_blacklist+0xa7/0xa7
[ 0.782059] [] ? rest_init+0x80/0x80
[ 0.782059] [] kernel_init+0x9/0xe0
[ 0.782059] [] ret_from_fork+0x3f/0x70
[ 0.782059] [] ? rest_init+0x80/0x80
[ 0.782059] Kernel Offset: disabled
[ 0.782059] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
ブートに失敗しました。QEMUを終了するには Ctrl-A x を押します。ちなみに、Ctrl-A h と押すとヘルプが表示され、Ctrl-A c と押すと QEMU monitor に入ります。QEMU monitor では、レジスタダンプやメモリダンプ、その他プロセッサの情報を取得することができます。
ブートメッセージには Unable to mount root fsと表示されています。ストレージデバイスを指定していませんので、ルートファイルシステムがないわけですね。
initrd (initramfs) は、Linuxカーネルが起動直後にメモリ上に読み込み(その名の通り)ラムディスクに展開するファイルシステムのイメージです。ブートローダが、カーネルと一緒に読み込みメモリ上に展開するので、カーネルが起動直後に使用可能になります。様々なストレージのためのデバイスドライバやファイルシステムを全てカーネルに組み込むとカーネルが巨大化してしまいますので、ターゲットシステムのルートファイルシステムをマウントするために必要となるドライバ類はinitrdが提供するようにすると、カーネル自体は必要最小限に抑えることができるようになります。
通常は、本来のストレージ上のルートファイルシステムをマウントした段階で、initrdが提供した仮のルートファイルシステムは不要になります。しかしながら、テスト用のプログラムを入れておき、実行することもできますので、ここではその目的のために使用します。
initrdのイメージは、その内容としたいディレクトリ構造をcpioでアーカイブし、gzipにより圧縮したものです。そこで、initrdを作成するためには、まず適当なディレクトリを作成し、initrdの内容とするファイルを集めます。例えば、シェルとしてbusyboxを使用することにすると、そのディレクトリをインストール先にします。以下は、そのようにして作成したものになります。
% ls
bin/ etc/ init* lib64/ lib@ sbin/ usr/
libはlib64へのシンボリックリンクです。initがinitrdの内容をマウント直後に実行するプログラムとなります。例えば以下の内容にします。
#!/bin/sh
[ -d /dev ] || mkdir -m 0755 /dev
[ -d /proc ] || mkdir -m 0755 /proc
[ -d /sys ] || mkdir -m 0755 /sys
echo "Mounting filesystems."
mount -a
mkdir /dev/pts
mount /dev/pts
exec /sbin/init
/sbin/init は busybox へのシンボリックリンクです。mount -a で /dev, /proc, /sys をマウントできるように、以下のetc/fstabも用意しておきます。
#
proc /proc proc nodev,noexec,nosuid 0 0
none /sys sysfs nodev,noexec,nosuid 0 0
none /dev devtmpfs mode=0755 0 0
none /dev/pts devpts noexec,nosuid,noauto 0 0
このディレクトリで以下のコマンドを実行するとinitrd.imgというファイル名でinitrdを作成できます。
% find . | cpio --quiet -H newc -o | gzip > ../initrd.img
QEMUにinitrdのファイルイメージを読み込ませるには -initrd オプションでinitrdファイルを指定します。
% qemu/x86_64-softmmu/qemu-system-x86_64 -m 256 -nographic -kernel linux-stable/arch/x86_64/boot/bzImage -append console=ttyS0 -initrd initrd.img
これでカーネルをブートすると、以下のようにカーネルがブート後にinitrdがマウントされシェルが使える状態になります。
Mounting filesystems.
can't run '/etc/init.d/rcS': No such file or directory
Please press Enter to activate this console.
/ # ls
bin dev etc init lib lib64 proc root sbin sys usr
/ #
/etc/init.d/rcSがないと怒られていますが、initの内容を移せば表示されなくなります。
今回は、Linuxカーネルのテスト実行を行うために、QEMUを用いる方法を紹介しました。ルートファイルシステムをinitrdとすることで、気軽にカーネルをブート、テスト実行、終了することができます。次回は、QEMUにGDBを接続してみましょう。
keisuke.kimura in Livox Mid-360をROS1/ROS2で動かしてみた
Sorry for the delay in replying. I have done SLAM (FAST_LIO) with Livox MID360, but for various reasons I have not be...
Miya in ウエハースケールエンジン向けSimulated Annealingを複数タイルによる並列化で実装しました
作成されたプロファイラがとても良さそうです :) ぜひ詳細を書いていただきたいです!...
Deivaprakash in Livox Mid-360をROS1/ROS2で動かしてみた
Hey guys myself deiva from India currently i am working in this Livox MID360 and eager to knwo whether you have done the...
岩崎システム設計 岩崎 満 in Alveo U50で10G Ethernetを試してみる
仕事の都合で、検索を行い、御社サイトにたどりつきました。 内容は大変参考になりま...
Prabuddhi Wariyapperuma in Livox Mid-360をROS1/ROS2で動かしてみた
This issue was sorted....