UMLによるはじめてのLinuxカーネルHack
はじめに
これまでにLinuxカーネルの解説本やカーネルのソースコードを読んでみたものの、
実際にカーネルをビルドしてブートする、という基本的なことを避けてきた。
Linuxカーネルをいじれるようになる(なりたい)、という観点で真剣に考えると、カーネルの
ビルドとビルドしたカーネルをブートするという行為は避けて通れない。
いじった結果を観測できないから。フィードバックが無ければやる気も維持できない。
何が障壁になっているのだろう?と考えると、自分でビルドしたカーネルを
Linuxにインストールして、ブートできなくなったらどうするの?面倒すぎる。
というのが一番の障壁になっていることがわかった。
この障壁を解決するいい方法は無いだろうか?と考えると、ちょうどいいのがあった。
それがUML(User Mode Linux)だった。
UMLを使えば、ビルドしたカーネルが何らかの原因でブートできなくなっても
大丈夫。大本のLinuxが、フリーズすることは無い。単にやり直せばいい。
UML上でカーネル開発をすると、試行錯誤に要するコスト(時間、精神的イライラ)が、
UMLを使わない場合と比較して、圧倒的に小さい。これがカーネル開発における、
UMLを用いる最大のメリットだと思う。
今回は、UbuntuにUMLを構築してみる。UML構築後、カーネルをいじる。
具体的には、printkを用いて、ブートメッセージに「Hello, World!」を出力してみる。
なお、用いたUbuntuは8.0.4。WindowsのVMWare Server 2上で長年使っているVMで、アップデートしていないので古い。
カーネルソースコードの取得
gitでカーネルソースコードを取得する。今、まさに開発中のコードをビルドした方が面白いので、そうした。cloneするのに結構時間がかかる。
% cd ~ % git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git linux-2.6
UMLカーネルのビルド
UML本家にあるUMLのビルド方法のドキュメントを見ながらUMLカーネルをビルドする。
http://user-mode-linux.sourceforge.net/source.html
ビルドは5分くらいで完了した。
% cd ~/linux-2.6 % make defconfig ARCH=um % make ARCH=um % ls -l linux -rwxr-xr-x 2 xxx xxx 25516656 2010-08-29 00:10 linux
UML環境構築
以下のドキュメントを見ながらUML環境を構築する。
https://help.ubuntu.com/community/UserModeLinux
UMLに必要なツールを用意。
% sudo apt-get install user-mode-linux uml-utilities bridge-utils debootstrap
% mkdir ~/uml-ubuntu % cd ~/uml-ubuntu
4GBのrootファイルシステムを作成。mkfs.xfsコマンドが無かったので、mkfs.ext3で代用。
% dd if=/dev/zero of=uml-root-hardy bs=4096 seek=1M count=1 % mkfs.ext3 uml-root-hardy
作成したファイルシステムを/mntにマウント。
% sudo mount -o loop uml-root-hardy /mnt
残りの作業はrootユーザーで作業する。
% sudo -i
マウントしたパーティションにベースシステムを構築。
ネットからパッケージをダウンロードしてくるので、数十分要した。
# debootstrap --arch i386 hardy /mnt http://us.archive.ubuntu.com/ubuntu
fstabに以下を追記。
# echo "/dev/ubd0 / xfs defaults 0 1" > /mnt/etc/fstab # echo "proc /proc proc defaults 0 0" >> /mnt/etc/fstab
rootファイルシステム用のデバイスファイルの作成とパーミッションの調整。
# mknod --mode=660 /mnt/dev/ubd0 b 98 0 # chown root:disk /mnt/dev/ubd0
hostsファイルに以下を追記。
# echo "127.0.0.1 localhost" > /mnt/etc/hosts
ネットワークインタフェースを設定。
# echo "auto lo" > /mnt/etc/network/interfaces # echo "iface lo inet loopback" >> /mnt/etc/network/interfaces # echo "auto eth0" >> /mnt/etc/network/interfaces # echo "iface eth0 inet dhcp" >> /mnt/etc/network/interfaces
Add first terminal and serial terminal to secure list.
何をしているのか理解できてない。
# echo "tty0" >> /mnt/etc/securetty # echo "ttys/0" >> /mnt/etc/securetty
Remove the other terminal events.
これも何をやっているのか理解できていない。
# rm /mnt/etc/event.d/tty2 # rm /mnt/etc/event.d/tty3 # rm /mnt/etc/event.d/tty4 # rm /mnt/etc/event.d/tty5 # rm /mnt/etc/event.d/tty6 # rm /mnt/etc/udev/rules.d/75-persistent-net-generator.rules
以上で完了と言いたいが、/mntをアンマウント。ドキュメントには書いてないけど、これをやっておかないと、UMLをブートできない。
# umount /mnt
UMLの起動
以下のコマンドでUMLを起動する。uml-root-hardy.cow1にuml-root-hardyのファイルサイズと同じ4GBを消費するので注意。
% cd ~/uml-ubuntu % ~/linux-2.6/linux ubd0=uml-root-hardy.cow1,uml-root-hardy umid=uml1
いくつかエラーメッセージが出ているが、今回はUMLが起動することを目標としたので、ひとまず無視。
Virtual Console #1(uml1)にログイン画面が表示された。
rootでログインできた。
UMLをシャットダウン。
# shutdown -h now
ひとまず、やりたかったことはできた。
ブートメッセージでHello, World!
詳解LINUXカーネル第3版(オライリー)によると、カーネルのブート時にstart_kernel()が実行されるらしい。
start_kernel()を眺めてみると、ブート時に表示されるカーネルのバナーのコードがあった。
(引用したコードの最後のprintk。)
% cat init/main.c [...] asmlinkage void __init start_kernel(void) { char * command_line; extern const struct kernel_param __start___param[], __stop___param[]; smp_setup_processor_id(); /* * Need to run as early as possible, to initialize the * lockdep hash: */ lockdep_init(); debug_objects_early_init(); /* * Set up the the initial canary ASAP: */ boot_init_stack_canary(); cgroup_init_early(); local_irq_disable(); early_boot_irqs_off(); early_init_irq_lock_class(); /* * Interrupts are still disabled. Do necessary setups, then * enable them */ tick_init(); boot_cpu_init(); page_address_init(); printk(KERN_NOTICE "%s", linux_banner); [...]
バナーの後に、「Hello, World!」を表示するようにしよう。diffは以下の通り。
今気づいたけど、git diffはsvn diffと違って、どの関数でのdiffなのかわかるようになっている。これは良い。
(@@ -566,6 +566,7 @@ asmlinkage void __init start_kernel(void)の所。)
% git diff diff --git a/init/main.c b/init/main.c index 94ab488..bf13e5e 100644 --- a/init/main.c +++ b/init/main.c @@ -566,6 +566,7 @@ asmlinkage void __init start_kernel(void) boot_cpu_init(); page_address_init(); printk(KERN_NOTICE "%s", linux_banner); + printk(KERN_NOTICE "Hello, World!"); setup_arch(&command_line); mm_init_owner(&init_mm, &init_task); setup_command_line(command_line);
Hello, World!を仕込んだカーネルをビルドする。クリーンしてビルドしていないので、1分くらいでビルドできた。
% cd ~/linux-2.6 % make ARCH=um $ ls -l linux -rwxr-xr-x 2 xxx xxx 25516656 2010-08-29 08:36 linux
UMLを起動する。バナーの後に、無事、「Hello, World!」が表示された。
% ~/linux-2.6/linux ubd0=uml-root-hardy.cow1,uml-root-hardy umid=uml1 Locating the bottom of the address space ... 0x10000 Locating the top of the address space ... 0xc0000000 Core dump limits : soft - 0 hard - NONE Checking that ptrace can change system call numbers...OK Checking syscall emulation patch for ptrace...OK Checking advanced syscall emulation patch for ptrace...OK Checking for tmpfs mount on /dev/shm...OK Checking PROT_EXEC mmap in /dev/shm/...OK Checking for the skas3 patch in the host: - /proc/mm...not found: No such file or directory - PTRACE_FAULTINFO...not found - PTRACE_LDT...not found UML running in SKAS0 mode Linux version 2.6.36-rc2-00237-gd4348c6-dirty (xxx@ubuntu-vm) (gcc version 4.2.4 (Ubuntu 4.2.4-1ubuntu3)) #4 Sun Aug 29 08:36:51 JST 2010 Hello, World!