qemu で armhf な Debian GNU/Linux (wheezy) を動かしてみた。
ホスト環境は Debian GNU/Linux (jessie) で、qemu-system-arm をインストールしておく。
Debian の armhf 用の installer をとってくる。
今回は qemu-system-arm でエミュレートする vexpress-a9 というマシンを使うので、それ用の installer をとってくる。(vexpress-a9 はメモリが 1G まで使えるのが良い。versatilepb は 256M までしか使えなくて厳しい)
% wget ftp://ftp.jp.debian.org/debian/dists/wheezy/main/installer-armhf/20130613+deb7u2+b1/images/vexpress/netboot/{vmlinuz-3.2.0-4-vexpress,initrd.gz}
Linux kernel と initrd であるが、これが installer である。CD や USB メモリのイメージではない。
disk image をつくる
以下で作っているのは raw で 8G なファイルという単純なものだが、LVM を使うとか他の選択肢もあるだろう。
% qemu-img create debian-arm.img 8G Formatting 'debian-arm.img', fmt=raw size=8589934592
installer を起動する
kernel と initrd をどうやって使うかというと、qemu-system-arm のオプションで指定する。まぁ、仮想マシンのメモリ内に適切に配置して実行できるなら特別に boot loader を使わなくてもいいのだろう。
% qemu-system-arm \ -M vexpress-a9 -m 1G \ -kernel vmlinuz-3.2.0-4-vexpress \ -initrd initrd.gz \ -drive file=debian-arm.img,if=sd,cache=writeback
起動すると、ウインドウが出てきて、Debian installer が起動する。
以下のようにオプションを足せば、curses を使うモードにもできる。(遠くのマシンでやるには便利)
% qemu-system-arm \ -M vexpress-a9 -m 1G \ -kernel vmlinuz-3.2.0-4-vexpress \ -initrd initrd.gz \ -append 'console=ttyAMA0' \ -drive file=debian-arm.img,if=sd,cache=writeback \ -curses -serial stdio
なお、vexpress の kernel は PCI をサポートしていないようで、-hda を使って disk を指定すると No disk drive was detected とか出てきてインストールできない。そのため、SDカードとして扱う。(参考: <URL:https://gist.github.com/bdsatish/7476239>) 単に -sd debian-arm.img じゃなくて -drive ... としているのは耐えがたいほど遅かったからである。
Debian installer と対話する
基本的に、普通にやればいい。
最後のほうで、No boot loader installed というメッセージが出て、/dev/mmcblk0p1 の /vmlinuz に root=/dev/mmcblk0p2 と指定して 起動するようにいわれる。
というわけで、install された kernel と initrd を外に取り出さないといけない。これをやる方法はいろいろあるだろうが、今回は reboot する前に (Alt-F2 で) shell を動かして、nc で取り出した。
外部の端末:
nc -l 9999 > boot.tar
QEMU中の端末
cd /target tar cf - boot | nc 外部のIPアドレス 9999
外部の端末:
tar xf boot.tar
(他の方法としては、qemu を止めた後に debian-arm.img から取り出すことが考えられる。)
取り出したら installer から reboot し、qemu を適当なところで Ctrl-C で止める。
install されたものを起動する
取り出した kernel と initrd を使って起動する。
% qemu-system-arm \ -M vexpress-a9 -m 1G \ -kernel boot/vmlinuz \ -initrd boot/initrd.img \ -append root=/dev/mmcblk0p2 \ -drive file=debian-arm.img,if=sd,cache=writeback \ -net nic -net user,hostfwd=tcp:127.0.0.1:3022-:22
これで動くのはごく普通の Debian で、(installer で外していなければ) sshd が動いている。qemu に hostfwd=tcp:127.0.0.1:3022-:22 と指定して、外側の 127.0.0.1:3022 から内側のマシンの 22 に port forward しているので、外側で ssh 127.0.0.1 -p 3022 とすると中にログインできる。
ウインドウを出さないで動かす
ウインドウが出るのは試すのにはいいのだが、動かしっぱなしにするにはうれしくない。
qemu は標準入出力を仮想マシンのシリアルと接続できるので、そのようにしてみる。
まず、arm な GNU/Linux では、シリアルは /dev/ttyAMA0 というデバイスのようである。なので、/etc/inittab に以下の行を足して ttyAMA0 にログインプロンプトを出すようにしておく。
AMA0:2345:respawn:/sbin/getty 38400 ttyAMA0
そして、qemu を以下のように起動する。
% qemu-system-arm \ -M vexpress-a9 -m 1G \ -kernel boot/vmlinuz \ -initrd boot/initrd.img \ -append 'root=/dev/mmcblk0p2 console=ttyAMA0' \ -nographic \ -drive file=debian-arm.img,if=sd,cache=writeback \ -net nic -net user,hostfwd=tcp:127.0.0.1:3022-:22
こうすると、qemu-system-arm の標準入出力が内部の仮想マシンの ttyAMA0 と接続され、起動した端末から普通にログインできる。
とりあえずこれを screen の中で飼えばいいかな。
file descriptor passing で、fd が受け取られないで消えてしまったらどうなるか試してみた。
端末1:
% uname -mrsv Linux 3.14-1-amd64 #1 SMP Debian 3.14.4-1 (2014-05-13) x86_64 % ruby -rsocket -e ' Socket.unix_server_loop("/tmp/s") {|s| p s p :before_sleep; t = Time.now sleep 3 p :after_sleep; p Time.now-t s.close }' #<Socket:fd 8> :before_sleep :after_sleep 3.000132032
端末2:
% ruby -rsocket -e ' s = Socket.unix("/tmp/s") r, w = IO.pipe s.sendmsg("\0", 0, nil, Socket::AncillaryData.unix_rights(w)) w.close p :before_read; t = Time.now p r.read p :after_read; p Time.now - t ' :before_read "" :after_read 3.000255096
なにをやっているかというと、端末1で起動した Unix domain socket server に、端末2で起動した client が接続し、その接続を通してパイプの書き込み側を送る。しかし、server は受け取れるパイプを受け取らずに、3秒後に接続されたソケットを close する。
client では、パイプの書き込み側は送った後にすぐに close する。そうすると、書き込み側はどのプロセスにも存在せず、kernel の中にしかない状態になる。その状態で読み込み側からデータを読み出そうとするとどうなるか。
結果としては、(だれもデータを書き込まないので) データは読み出せず、書き込み側を取り出せるプロセスが存在しなくなった時点で EOF が検出される。
まぁ、順当な結果だろう。
[latest]