distccを用いた分散コンパイル環境の構築

カーネルを触っていると、コンパイル時間が開発のボトルネックになってしまうことがあります. この問題を解決するために、distccを用いた分散コンパイル環境を構築してみました. なお、ファイルパスなどはすべてubuntuのものとなっており、分散先のマシンのgccのバージョンは一致している必要があります.

debパッケージをインストールする

distccをdebパッケージ経由でインストールします. 今回インストールしている ccache は今回の件には直接関係はないのですが、メモリ上にコンパイル済みのオブジェクトを保持しておき、コンパイル速度を早める効果があるため、ついでにインストールしてみました.

 $ aptitude install distcc ccache

設定ファイルを書く

/etc/default/distcc が設定ファイルです. このファイルに設定を書くことで、distccデーモン起動時、明記的にオプションを与える必要がなくなります.

# Defaults for distcc initscript
# sourced by /etc/init.d/distcc

#
# should distcc be started on boot?
#
# STARTDISTCC="true"

STARTDISTCC="true"

#
# Which networks/hosts should be allowed to connect to the daemon?
# You can list multiple hosts/networks separated by spaces.
# Networks have to be in CIDR notation, f.e. 192.168.1.0/24
# Hosts are represented by a single IP Adress
#
# ALLOWEDNETS="127.0.0.1"

# 同一LAN内からのコンパイル要求を通過させる
ALLOWEDNETS="127.0.0.1 192.168.1.0/24"

#
# Which interface should distccd listen on?
# You can specify a single interface, identified by it's IP address, here.
#
# LISTENER="127.0.0.1"

# Listen するインターフェースのIPアドレスを指定する.
# 空文字にするとすべてのインターフェースでListenを行う.
LISTENER=""

#
# You can specify a (positive) nice level for the distcc process here
#
# NICE="10"

NICE="10"

#
# Enable Zeroconf support?
# If enabled, distccd will register via mDNS/DNS-SD.
# It can then automatically be found by zeroconf enabled distcc clients
# without the need of a manually configured host list.
#
# ZEROCONF="true"

ZEROCONF="true"

環境変数の設定

処理を分散させたいホスト名を、環境変数 DISTCC_HOSTS にスペース区切りで指定します.

export DISTCC_HOSTS='localhost machine1.hoge.net machine2.hoge.net'

コンパイルしてみる

  • 何もない場合
$ make -j4 bzImage CC="ccache gcc"
real    6m6.095s
user    10m45.380s
sys     1m0.672s

Core 2 Duo machine 2GHz memory 2GB
  • distccを用いた場合
$ make -j40 bzImage CC="ccache distcc"
real    2m38.123s
user    3m33.881s
sys     0m43.043s

Core i7 2.66GHz memory 12GB
Core 2 Duo  3GHz memory 4GB x 3
Core 2 Duo  2GHz memory 2GB

素晴らしい!2倍以上の速度がでました. カーネルに限らず、大変有用ですね.

パッチの返信が返ってきた!

先日投げたパッチですが、なんと 開発者の Avi さんから返信がありました! 日本語版のユーザドキュメントしか見てなかったので知りませんでしたが、 -drive オプションなんてできていたのですねー(日本語版のユーザドキュメントを見ると、-driveオプションはまだないことがわかる). なかなか楽しい経験ができて、良かったです!

次はこういった表面的なパッチではなくて、ちゃんとしたパッチを投げていきたいです:D

gdbstub: packet reply is too long

qemu に -s オプションをつけると、qemu の gdbstub が localhost:1234 で立ち上がり、gdb 上で target remote localhost:1234 などとすると接続できますよね. しかし、kvm-88の時点で、現在の qemu では、これを利用することができません. 具体的には、以下のような挙動に陥ります:

(gdb) target remote localhost:1234
Remote debugging using localhost:1234
[New Thread 1]
Remote 'g' packet reply is too long: bae1120000000000000000000000000000000000000000000600000000000000645267b9000000000100000000000000149fbcc70000000000
9fbcc70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0e11fc00
00000008302000060000000680000007b0000007b000000d8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000040cf16407f03000000380000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000801f0000

そして、エラー名で検索してみると、以下のスレッドが出てきます:

http://www.mail-archive.com/kvm@vger.kernel.org/msg08717.html

まとめると、

  • 解決策はあるにはあるが、根本的な解決策ではない. 修正するなら、もっと根本的に変更するべきだ.
  • 私の直したkvmのソースを見てもいいよ.
    • [1] git://git.kiszka.org/kvm-userspace.git gdb-queue
    • [2] git://git.kiszka.org/qemu.git gdb-queue

というわけで、修正がマージされていない様子です. かなり使える機能なので、早く修正されて欲しい気持ちはありますが、確かに保守する方はかなりしんどいのではないかと...(switch...case文で、数値直打ちで分岐してたしなぁ...w)

なお、修正版も試してみたのですが、私の環境では同様のエラーが出てしまいました. まとめると、現段階では動作しないのかな、という結論です.

KVMを用いて、カーネルイメージから直接起動したい...!(解決編) + パッチ投げた

先日の記事で、KVMからの直ブートに失敗していましたが、無事に動作しました!

/usr/local/kvm/88/bin/qemu-system-x86 -kernel /boot/vmliuz-2.6.28-15-generic -initrd /boot/initrd.img-2.6.28-15-generic -hda ~/disks/ubuntu.img -append "root=/dev/sda"

でブートします. 前回犯していた過ちは、ルートデバイスを "/dev/hda" と示していたことです.

busybox中で、

$ ls /dev/hda

としても、としても、何も出てこなかったのでおかしいなぁとは思っていましたがw
id:viverid:kidminにアドバイス頂き、再度 ls し直してみると...

$ ls /dev/ | grep da
sda
sda1
sda2
sda3

と出てきました!そりゃうごきませんわ...w
というわけで、起動デバイスファイルがsdaにも関わらずhdaを指定するのは非直感的であると思ったため、以下のようなパッチを作成しました.

diff --git a/qemu-options.hx b/qemu-options.hx
index c1ec976..91cd931 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -68,6 +68,13 @@ Use @var{file} as floppy disk 0/1 image (@pxref{disk_images}). You can
 use the host floppy by using @file{/dev/fd0} as filename (@pxref{host_drives}).
 ETEXI
 
+DEF("sda", HAS_ARG, QEMU_OPTION_sda,
+    "-sda/-sdb file  use 'file' as IDE hard disk 0/1 image\n")
+DEF("sdb", HAS_ARG, QEMU_OPTION_sdb, "")
+DEF("sdc", HAS_ARG, QEMU_OPTION_sdc,
+    "-sdc/-sdd file  use 'file' as IDE hard disk 2/3 image\n")
+DEF("sdd", HAS_ARG, QEMU_OPTION_sdd, "")
+
 DEF("hda", HAS_ARG, QEMU_OPTION_hda,
     "-hda/-hdb file  use 'file' as IDE hard disk 0/1 image\n")
 DEF("hdb", HAS_ARG, QEMU_OPTION_hdb, "")
diff --git a/vl.c b/vl.c
index 3485ce6..62d1d91 100644
--- a/vl.c
+++ b/vl.c
@@ -5085,6 +5085,7 @@ int main(int argc, char **argv, char **envp)
                 initrd_filename = optarg;
                 break;
             case QEMU_OPTION_hda:
+            case QEMU_OPTION_sda:
                 if (cyls == 0)
                     hda_opts = drive_add(optarg, HD_ALIAS, 0);
                 else
@@ -5096,6 +5097,9 @@ int main(int argc, char **argv, char **envp)
                              translation == BIOS_ATA_TRANSLATION_NONE ?
                                  ",trans=none" : "");
                  break;
+            case QEMU_OPTION_sdb:
+            case QEMU_OPTION_sdc:
+            case QEMU_OPTION_sdd:
             case QEMU_OPTION_hdb:
             case QEMU_OPTION_hdc:
             case QEMU_OPTION_hdd:

なお、パッチの適応先は 2009/09/07 時点での最新レポジトリです. このパッチを適応することで、

/usr/local/kvm/88/bin/qemu-system-x86 -sda ~/disks/ubuntu.img

といった風に、-sdaをオプションでディスクイメージを指定することができます. その他, sdb,sdc,sdd に対応しています. なお、このパッチをKVMのMLに投げてみました. スルーされるかもしれませんが、どうなるか楽しみですw

KVMを用いて、カーネルイメージから直接起動したい...!(未解決)

というのは、需要としてはかなりありますよね. というわけで、以下のオプションを用いることでブートできる...はずなのですが!

/usr/local/kvm/88/bin/qemu-system-x86 -kernel /boot/vmliuz-2.6.28-15-generic -initrd /boot/initrd.img-2.6.28-15-generic -hda /dev/zero -append "root=/dev/hda rootdelay=90"

丁度virtio-pciを読みこんだ後に、

Begin: Loading essential drivers...
done.
...
Begin: Mounting for root file system
...
Begin: Waiting for root file system
Gave up wating for root device. Common problems:
 - Boot args (cat /proc/cmdline)
   - Check rootdelay= (did the system wait long enough?)
   - Check root = (did the system wait for the right device?)
 - Missing modules (cat /proc/modules; ls /dev)
ALERT! /dev/root does not exit. Dropping to a shell!

というメッセージが出て、busyboxが起動. どうしたらいいのだろう. こういうときはドキュメントを読んでみます. まずは initramfs のドキュメント

全ての 2.6 系 Linux カーネルgzip 圧縮された「cpio」フォーマットのアー
カイブを含んでおり、これはカーネルが起動する際に rootfs に展開されます。
展開後、カーネルは rootfs が「init」ファイルを含んでいるかどうか確認し、
あれば PID 1 ※として実行されます。

by http://www.linux.or.jp/JF/JFdocs/kernel-docs-2.6/filesystems/ramfs-rootfs-initramfs.txt.html

ということは、rootfs を見つける前段階でコケてるってことですね. 次に、qemuのオプションを見てみます.

Use ‘-kernel’ to provide the Linux kernel image and ‘-append’ to give the kernel command line arguments. The ‘-initrd’ option can be used to provide an INITRD image.

When using the direct Linux boot, a disk image for the first hard disk ‘hda’ is required because its boot sector is used to launch the Linux kernel.

by http://www.qemu.org/qemu-doc.html#SEC34

めぼしい情報はみつからず. ドライバを確認してみるかなぁ.

Linux カーネル 2.6.30.5 で kvm-88 のインストールが通らない

タイトルの通りです. make installしてみると、

$ make install
WARNING: Loop detected: /lib/modules/2.6.30.1/extra/kvm.ko which needs kvm.ko 
again!
WARNING: Module /lib/modules/2.6.30.1/extra/kvm.ko ignored, due to loop
WARNING: Module /lib/modules/2.6.30.1/extra/kvm-intel.ko ignored, due to loop
WARNING: Module /lib/modules/2.6.30.1/extra/kvm-amd.ko ignored, due to loop

となり、depmodに失敗します. この問題は、こちらのパッチを当てることで回避できます:

diff -Nur kvm-88.orig/kvm/kernel/external-module-compat-comm.h kvm-88/kvm/kernel/external-module-compat-comm.h
--- kvm-88.orig/kvm/kernel/external-module-compat-comm.h	2009-07-17 20:47:08.000000000 -0300
+++ kvm-88/kvm/kernel/external-module-compat-comm.h	2009-07-17 20:55:21.000000000 -0300
@@ -845,7 +845,7 @@ 
 
 #include <linux/tracepoint.h>
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)
 
 struct tracepoint;
 


by http://patchwork.kernel.org/patch/36141/

どうも、kvm-88/kvm/kernel/external-module-compat-comm.h が、Linux 2.6.29 までしか対応していないようです. リリース時期を考えるとしょうがないでしょう. このファイルをpatchとして保存し、

$ patch -p1 -d 88 < patch

として、再度

$ sudo make install

とすると、depmod に成功します. なお、このパッチのライセンスはGPLになると思われます.

カーネルコンパイルについて

Debian系のディストリビューションにおいて、マシン間で動作するカーネルコンパイルしようと思ったら、通常は"make-kpkg"コマンドを利用しますよね. ちょっとしたオプションを付け加えるだけで deb パッケージを作成できるため、大変便利なのですが、2つ欠点があります. それは、

という点です. 遅い原因は、恐らくカーネルだけでなく、モジュール/設定ファイル/バージョン情報もインストールするためでしょう. これを回避するには、やはり標準の make を用いて、カーネルだけをコンパイルするのが良いように思われます. そんなわけで、カーネルソースのルートディレクトリで実行すると、kernel+moduleのtar.gzができあがるシェルスクリプトを書いてみました. ソースは私のgithubからダウンロードすることができます. これと、kernelのイメージを作成するシェルスクリプトを用いれば、カーネルだけを換装可能な状態に持って行けるので、コンパイル時間を大幅に減らすことができます(最初からそうしろという話もあるw

このシェルスクリプトを書くにあたっては、 id:viver の助言が大変ためになりました. ありがとうございます!