Linuxカーネルの開発環境
Linuxのソースはtarballを拾ってくれば簡単に読めるのに肝心のカーネルをいじろうと思うと開発環境構築の日本語ドキュメントも少なくちょっと大変ですよね? という事でqemuを使った簡単Linuxカーネル開発環境の構築手法を載せておきます。(僕の開発PCはこれの実験最中にMBR書き換えで起動出来なくなってますので十分注意してください... grubを弄らなければたぶん大丈夫ですが)
ちなみにこの作業はLinuxの開発環境構築に止まらず、気合の入った人なら完全オリジナルディストリビューション構築の手がかりになるはずです。
事前に用意するのは以下になります。
※括弧内は僕の環境です
開発環境構築には大きく分けて以下のような2つの工程があります。
Linuxカーネルイメージの用意
まずLinuxカーネルイメージの用意ですが、これはLinuxのカーネル再構築をした事がある人にはお馴染みの方法で、カーネルのソースがあるディレクトリで以下のコマンドを打ちます
$ sudo make menuconfig $ sudo make dep $ sudo make bzImage
まず「make menuconfig」でカーネルに何を組み込むかの設定をします。ここで色々詰め込むとコンパイルに時間が掛かるのでとりあえずファイルシステムのext2以外は外してしまいましょう。*3
「make menuconfig」を行うとそのディレクトリに「.config」というファイルが生成されています。「make dep」でこの「.config」に合わせた依存関係を解決し、最後に「make bzImage」でカーネルをコンパイルします。イメージは
/usr/src/linux/arch/i386/boot/bzImage ※/usr/src/linuxをLinuxソースを展開したディレクトリとします。
に生成されています。このファイルは圧縮されたカーネル本体の先頭にbootsect.Sとsetup.Sをくっつけた物と思ってください。Kernel 2.4の頃はこのイメージが1.44MB以下なら1FDでブート出来たのですが、Kernel 2.6では出来ないようになっています。
そこでLinuxのgrubを使ってこのbzImageを「/boot」に置き、「/boot/grub/grub.conf」を書き換えてPC再起動して...
とカーネル再構築の場合はこうゆう流れになるのですが、カーネル自体を弄る場合は面倒臭い事この上ないですね。というわけでqemuを使います。
qemuにはカーネルのbzImageを直接読み込めるオプション「-kernel」が存在します。ただそのままでは読めません。ちょっとしたおまじないが必要でそれが「-append」です。正確にはカーネルオプションと呼ばれていてこれにroot=やらinit=やらを追加します。
具体的には
$ qemu -kernel bzImage -append "root=/dev/hda" fs.img
とします。
fs.imgとはディスクイメージでext2などのファイルシステムを一つのファイル上にそのままコピーしたイメージです。qemuはディスクイメージとして渡すファイルを1番目のディスクの1番目のパーティションとして認識します。これはhdaという識別名が与えられていてそれをカーネルオプションでrootにマウントします。
では次にfs.imgの作成方法を見てみます
qemu用ディスクイメージの準備
ファイルシステムとしてはext2にします。まず以下のようにして20MBほどのファイルを用意します。
$ dd if=/dev/zero of=fs.img bs=20m
次にこのファイルをext2ファイルシステムでフォーマットします。
$ sudo mkfs.ext2 -F -q fs.img
こうするとfs.img上に「lost+found」ディレクトリのみが生成されたファイルシステムが作られます。これだけだと先ほどのqemuにより起動出来ません。なぜならbzImageカーネルを起動中には様々な共有ライブラリや「/sbin/init」「/bin/sh」などの実行ファイルが必要になるからです。これをどっかからコピーしてきます。開発環境のLinuxからコピーしてきてもいいのですが、依存関係が複雑で面倒臭いのでqemuに付属しているlinux.imgを使います。
まずfs.imgとlinux.imgを以下のようにマウントします。
$ sudo mkdir /mnt2 $ sudo mount fs.img /mnt -t ext2 -o loop=/dev/loop0 $ sudo mount linux.img /mnt2 -t ext2 -o loop=/dev/loop1
loopデバイスを使ってマウントする事によってlinux.img内のファイルをfs.imgにコピーする事が出来ます。
$ cd /mnt $ sudo mkdir dev $ sudo mkdir bin $ sudo mkdir sbin $ sudo mkdir lib $ sudo mkdir etc $ cd /mnt2 $ sudo cp lib/* /mnt/lib/ $ sudo cp bin/* /mnt/bin/ $ sudo cp etc/fstab /mnt/etc/ $ sudo cp sbin/* /mnt/sbin/
またキャラクタデバイスのconsoleとttyを以下のように作成します。
$ cd /mnt/dev $ sudo mknod -m 622 console c 5 1 $ sudo mknod -m 622 tty0 c 4 0
僕の場合は以下のようなファイル構成にしてます。
-- bin | ||
-- cat | ||
-- cp | ||
-- echo | ||
-- ls | ||
-- mount | ||
-- ps | ||
-- rm | ||
-- sh | ||
`-- umount | ||
-- boot | ||
-- grub | ||
-- e2fs_stage1_5 | ||
-- grub.conf | ||
-- splash.xpm.gz | ||
-- stage1 | ||
`-- stage2 | ||
-- initrd.img | ||
`-- vmlinuz | ||
-- dev | ||
-- console | ||
`-- tty0 | ||
-- etc | ||
-- fstab | ||
`-- mtab | ||
-- lib | ||
-- kbd | ||
`-- fr-pc.map | ||
-- ld-2.3.2.so | ||
-- ld-linux.so.2 -> ld-2.3.2.so | ||
-- libc-2.3.2.so | ||
-- libc.so.6 -> libc-2.3.2.so | ||
-- libdl-2.3.2.so | ||
-- libdl.so.2 -> libdl-2.3.2.so | ||
-- libm-2.3.2.so | ||
-- libm.so.6 -> libm-2.3.2.so | ||
-- libnss_compat-2.3.2.so | ||
-- libnss_compat.so.2 -> libnss_compat-2.3.2.so | ||
-- libnss_files-2.3.2.so | ||
-- libnss_files.so.2 -> libnss_files-2.3.2.so | ||
-- libproc.so.2.0.11 | ||
-- libpthread-0.10.so | ||
-- libpthread.so.0 -> libpthread-0.10.so | ||
-- libresolv-2.3.2.so | ||
-- libresolv.so.2 -> libresolv-2.3.2.so | ||
-- librt-2.3.2.so | ||
-- librt.so.1 -> librt-2.3.2.so | ||
-- libselinux.so.1 | ||
-- libsepol.so.1 | ||
-- libtermcap.so.2 -> libtermcap.so.2.0.8 | ||
-- libtermcap.so.2.0.8 | ||
`-- modules | ||
-- lost+found [error opening dir] | ||
-- proc | ||
-- root | ||
-- sbin | ||
-- init | ||
`-- init.org |
ここまでくれば恐らく(書き忘れが無ければ)シェル(/bin/sh)が起動します。
あとはカーネルを書き換えたらmake bzImageしてqemuで起動するだけの状態になります。
おまけとしてgrubを使えばqemuの「-kernel」オプションやらを使わなくても起動出来るようになるのですが僕のPCみたいになる可能性もあるため十分注意して行ってください...
あとbootsect.Sとsetup.Sを書き換えて-fdaで起動するというのも楽しいです。
※2.4のsetup.Sのhead.Sへのljmpですが、そのまま行うと0x60のセグメントへジャンプしてますがそれだとsetup.Sで定義していないセグメントになるので0x10としてください。あとbootsect.Sでsetupのセクタ数の書き換え等色々罠があります。