2010年02月25日

QEMUのもうひとつの使い方: ユーザーモードエミュレーションとbinfmtとchrootの組み合わせ

QEMUの上でDebianなどが動いていると、apt-get で簡単にコンパイラなどもインストールすることができます。しかし、そこで実際にビルドを行うととんでもなく時間がかかります。一晩かけても終わらないこともあります。

QEMUには今まで紹介してきたような、システム全体をエミュレートするものの他に、Linuxのユーザーモードのみをエミュレートするものがあります。今回はユーザーモードエミュレーションを紹介します。

ユーザーモードエミュレーションを利用することでビルドにかかる時間を1/10に短縮することができました。



rootstockに隠された技

前回Ubuntuのインストールの時にルートファイルシステムを構築するのにrootstockというコマンドを使いました。これはshell scriptなので何をやっているのかを見ることができます。

debootstrapというコマンドでルートファイルシステムを構成するパッケージを取ってくるのですが、それぞれのパッケージを配置した後にパッケージ内のスクリプトを必要なものがあります。そのスクリプトをクロス環境で実行するために、QEMUを使っています。

実は同じrootstockでも9.04と9.10では動作が異なります。9.04ではQEMUが必要なところでは全てシステムエミュレーション(qemu-system-arm)を利用しています。9.10では可能な部分ではユーザーモードエミュレーションのqemuを使っています。ユーザーモードエミュレーションのqemuを使うところは/usr/bin/build-arm-chrootというコマンドにまとめられています。これもshell scriptです。

私がなぜこの違いに気がついたかといえば、それは実行時間が明らかに違っていたからです。正確には測っていませんが、同じ規模のルートファイルシステムを作るのに、9.10では9.04の1/3くらいの時間で済みました。

ユーザーモードエミュレーションQEMU + binfmt + chroot

Linuxのbinfmtの仕組みを使うとARMのバイナリを実行しようとしたときに自動的にユーザーモードエミュレーションQEMUを介在させて動かすことができます。この仕組みはWineでWindowsのexeファイルを実行できるようにするときにも使われています。

さらにchrootというコマンドを使うと、あるディレクトリをあたかもルートディレクトリであるかのようにみせかけることができます。

スタティックリンクされたユーザーモードエミュレーションのQEMU

通常のユーザーモードエミュレーションQEMUの実行ファイルは以下のようにダイナミックリンクされています。

$ file /usr/bin/qemu-arm
/usr/bin/qemu-arm: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
 dynamically linked (uses shared libs), for GNU/Linux 2.6.15, stripped
$ 
$ ldd /usr/bin/qemu-arm
	linux-vdso.so.1 =>  (0x00007fff4ffff000)
	libm.so.6 => /lib/libm.so.6 (0x00007fab7755f000)
	libpthread.so.0 => /lib/libpthread.so.0 (0x00007fab77343000)
	librt.so.1 => /lib/librt.so.1 (0x00007fab7713b000)
	libc.so.6 => /lib/libc.so.6 (0x00007fab76dc9000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fab777e4000)
$ 

つまり、/usr/bin/qemu-armを実行するためにはこれらのダイナミックリンクライブラリが必要ですが、ARMだけの実行ファイルがあるルートファイルシステムにchrootしようとすると、これらのライブラリがARMのものと重なってしまいます。

でもこの問題を実にスマートに解決するのが、スタティックリンクされたユーザーモードエミュレーションQEMUを使うことです。それが/usr/bin/qemu-arm-staticです。このファイルをひとつだけchrootする先のディレクトリツリーにコピーしておけば動きます。

$ file /usr/bin/qemu-arm-static 
/usr/bin/qemu-arm-static: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
 statically linked, for GNU/Linux 2.6.15, stripped

bimfmt

ARMのバイナリ用のbimfmtの登録は以下のようになっています。

$ update-binfmts --display arm
arm (enabled):
     package = qemu-arm-static
        type = magic
      offset = 0
       magic = \x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00
        mask = \xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff
 interpreter = /usr/bin/qemu-arm-static
    detector = 
$ 

chrootでarmのbashを動かす

/export/debian_lenny_armel/root は前回NFSrootのときに作成しました。この下にある/bin/bash はもちろんARMのバイナリです。

$ file /export/debian_lenny_armel/root/bin/bash 
/export/debian_lenny_armel/root/bin/bash: ELF 32-bit LSB executable, ARM, version 1 (SYSV),
 dynamically linked (uses shared libs), for GNU/Linux 2.6.14, stripped
$ 

これをchrootして動かしてみます。Ubuntu 9.10ではsudo apt-get install rootstock とすれば必要な設定は全て完了しています。

 $ uname -m
 x86_64
 $ ROOT=/export/debian_lenny_armel/root
 $ sudo cp /usr/bin/qemu-arm-static $ROOT/usr/bin
 $ sudo chroot $ROOT /bin/bash
 # 
 # uname -m
 armv5tel

chrootの前後でuname -mで表示されるCPUアーキテクチャのタイプが変わっているのがわかります。

実際にconfigureしてビルド。その時間を比較

スーパーユーザーのままビルドを行うのは嫌なので、一般ユーザのアカウントにsu しておきます。

 # su user
 $

今回はbinutilsのソースアーカイブを展開して、それをconfigureしてネイティブビルドしたときの時間を比較してみます。

$ tar xvf binutils-2.20.tar.bz2
$ mkdir obj
$ cd obj
$ ../binutils-2.20/configure --disable-werror --disable-nls --disable-shared
$ time make

最初はシステムエミュレーションのQEMU(qemu-system-arm)です。

ARMのkernelからまるごとひとつのプロセスのひとつのスレッドで動いています。

real	80m40.513s
user	46m53.320s
sys	28m37.350s

次に、ユーザーモードエミュレーションのQEMU(qemu-arm-static + chroot)

real	28m14.252s
user	26m54.345s
sys	1m2.736s

実際に使用しているホストのLinuxはクワッドコアなので、makeに-j4オプションを付加してみます。

qemu-arm-static + chroot (make -j4)

real	8m48.824s
user	25m15.499s
sys	0m56.852s

最初の例で80分かかっていたものが、8分にまで短縮できました。

最後の例でmake -j4 の実行中に他の端末からps uコマンドでプロセスの様子を見てみました。

koba     13651  0.0  0.1  21452  4532 pts/1    Ss+  11:44   0:00 bash
koba     19336  0.8  0.1 129984  4176 pts/15   S+   21:25   0:00 /usr/bin/qemu-arm-static /bin/bash -c r=`${PWDCMD-pwd}`; export r; \?s=`cd ../binutils-2.2
koba     19342  0.3  0.0 130012  3400 pts/15   S+   21:25   0:00 /usr/bin/qemu-arm-static /bin/bash -c r=`${PWDCMD-pwd}`; export r; \?s=`cd ../binutils-2.2
koba     19352  7.8  0.1 129668  4608 pts/15   S+   21:25   0:00 /usr/bin/qemu-arm-static /usr/bin/make DESTDIR= RPATH_ENVVAR=LD_LIBRARY_PATH TARGET_SUBDIR
koba     19683 15.8  0.1 129548  4504 pts/15   S+   21:25   0:00 /usr/bin/qemu-arm-static /usr/bin/make all-recursive
koba     19684  2.2  0.1 129952  4436 pts/15   S+   21:25   0:00 /usr/bin/qemu-arm-static /bin/bash -c failcom='exit 1'; \?for f in x $MAKEFLAGS; do \?  ca
koba     19692 20.0  0.1 129700  4648 pts/15   S+   21:25   0:00 /usr/bin/qemu-arm-static /usr/bin/make all-am
koba     19706 20.5  0.1 130996  5496 pts/15   S+   21:25   0:00 /usr/bin/qemu-arm-static /bin/bash ./libtool --tag=CC --mode=compile gcc -DHAVE_CONFIG_H -
koba     19707 21.5  0.1 131012  5516 pts/15   S+   21:25   0:00 /usr/bin/qemu-arm-static /bin/bash ./libtool --tag=CC --mode=compile gcc -DHAVE_CONFIG_H -
koba     19708 19.5  0.1 130996  5496 pts/15   S+   21:25   0:00 /usr/bin/qemu-arm-static /bin/bash ./libtool --tag=CC --mode=compile gcc -DHAVE_CONFIG_H -
koba     19709 20.0  0.1 130996  5500 pts/15   S+   21:25   0:00 /usr/bin/qemu-arm-static /bin/bash ./libtool --tag=CC --mode=compile gcc -DHAVE_CONFIG_H -
koba     19776  0.5  0.0 128056  3380 pts/15   S+   21:25   0:00 /usr/bin/qemu-arm-static /usr/bin/gcc -DHAVE_CONFIG_H -I. -I../../binutils-2.20/bfd -I. -I
koba     19778  1.5  0.0 128060  3384 pts/15   S+   21:25   0:00 /usr/bin/qemu-arm-static /usr/bin/gcc -DHAVE_CONFIG_H -I. -I../../binutils-2.20/bfd -I. -I
koba     19780 66.5  0.5 144392 20980 pts/15   R+   21:25   0:01 /usr/bin/qemu-arm-static /usr/lib/gcc/arm-linux-gnueabi/4.4.1/cc1 -quiet -I. -I../../binut
koba     19781  1.5  0.0 128056  3384 pts/15   S+   21:25   0:00 /usr/bin/qemu-arm-static /usr/bin/gcc -DHAVE_CONFIG_H -I. -I../../binutils-2.20/bfd -I. -I
koba     19783  1.5  0.0 128052  3376 pts/15   S+   21:25   0:00 /usr/bin/qemu-arm-static /usr/bin/gcc -DHAVE_CONFIG_H -I. -I../../binutils-2.20/bfd -I. -I
koba     19784 63.0  0.7 142640 30572 pts/15   R+   21:25   0:01 /usr/bin/qemu-arm-static /usr/lib/gcc/arm-linux-gnueabi/4.4.1/cc1 -quiet -I. -I../../binut
koba     19786 66.0  0.7 142776 30664 pts/15   R+   21:25   0:01 /usr/bin/qemu-arm-static /usr/lib/gcc/arm-linux-gnueabi/4.4.1/cc1 -quiet -I. -I../../binut
koba     19787 64.0  0.5 143956 20976 pts/15   R+   21:25   0:01 /usr/bin/qemu-arm-static /usr/lib/gcc/arm-linux-gnueabi/4.4.1/cc1 -quiet -I. -I../../binut
koba     19788  0.0  0.0  15436  1140 pts/16   R+   21:25   0:00 ps u

圧巻ですね。bashやgccなどがqemu-arm-static を介して動いている様子がよくわかります。

参考までに、binutils-2.20を同じLinuxマシンでネイティブビルドした時の時間は以下の通りです。(make -j4) この差分がqemu-arm-static によるオーバーヘッドと見ることができます。

real	0m36.680s
user	1m16.541s
sys	0m27.254s

2010.6.10 追記

CELFテクニカルジャンボリーでこれを紹介しました。

CELFテクニカルジャンボリーで QEMUの話をしました



トラックバックURL

トラックバック一覧

1. [qemu]ユーザーモードqemu+chrootの環境改善  [ 組み込みの人。 ]   2010年02月27日 19:33
会社のブログに書いたKMC Staff Blog:QEMUのもうひとつの使い方: ユーザーモードエミュレーションとbinfmtとchrootの組み合わせの続き。 マニアックになりすぎかもと思ったのでこっちに書きました。 不便に思ったところが少し改善できました。 /procをmountする 単にchroot

コメントする

名前
URL
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード