メインコンテンツまでスキップ

gem5 FSモードのdisk imageをDockerを使って作成する方法

· 約10分

アーキテクチャシミュレータ gem5 には,Systemcall Emulation (SE) モードと Full System (FS) モードの2種類があります.
今回は,FSモードに必要な disk image を,Docker を使って作成する方法を紹介します.

公式ドキュメントで紹介されている方法

gem5 は公式のドキュメントが非常に豊富です.
disk image の作成方法についても,こちら に 3 種類も紹介されています 1

手法だけ簡単に紹介すると,

  1. gem5 utils と chroot を使う方法
  2. QEMU を使用する方法
  3. Packer を使用する方法 の 3 つです. 3番目は gem5art でもこちらに紹介されているので,馴染みのある方もいらっしゃるかもしれません.

Packer を使えば,イメージサイズや OS などを JSON で宣言した上で packer build config.json するだけで済むので,非常に手軽です.

ただ,この方法には時間がかかります.
Qemu などの仮想マシンを立ち上げて Ubuntu など選択した OS のインストールメディアを起動し,仮想キーボードデバイスを介してインストール時の初期設定を進めていくことになります.
仮想マシンは一般に,システムコールを呼び出して特権モードで CPU を利用すると低速になりがちです.
従って,ディスクアクセスの頻繁なインストール作業は遅いわけです.
筆者の環境では,こちらに紹介されている,post-installation でほとんど何も行わないケースでも 7分30秒 かかりました.

そこで登場するのが, Docker を使った方法です.

Docker-to-linux を利用する

OS のインストールに時間がかかるのなら,最初から OS がインストールされた環境から始めれば良いのではないか,というのは自然な発想です.
Docker を使うと,そんな環境が簡単に手に入ります.

そんな Docker ですが,そのままではコンテナなので,ブート可能な disk image ではありません.
これを強引にも変換するためのスクリプトを GitHub で見つけたので,こちらで紹介します.
docker-to-linux というものです.
これは,Ubuntu などの コンテナイメージを docker export でダンプし,そこに botloader を書き込む,ということを行っているようです.

使い方

使い方は非常にシンプルです.
Ubuntu のイメージを作りたかったら,make ubuntu とするだけです.
デフォルトで Alpine, Debian, Ubuntu の 3つの Dockerfile が用意されています.
gem5で実行するためのベンチマークなどを入れたかったら,同じようにして Dockerfile を用意して,ベンチマークのビルドやコピーを行えば可能です.

作成した disk image を QEMU で動作確認するには,qemu-system-x86_64 -enable-kvm -m 8G -drive format=raw,file={{disk-image}} とすれば可能です.
起動したら,ユーザroot,パスワードrootでログインできるはずです.

ハマった点1: NFSのパーミッション設定

自分の laptop では上記の手順で disk image が作成できるのに,研究室のサーバで実行すると,dd: failed to open '/os/ubuntu.img': Permission denied とパーミッションのエラーが発生してしまいました.

原因を探るため,スクリプトに沿って手作業で進めました.
その結果,Docker の bind mounting で $(pwd):/os:rw とした上で rootユーザとして touch /os/foo とすると,permission deniedとなってしまうことが分かりました.
本来,rootユーザはパーミッションに関係なくファイル操作が可能なはずなので,おかしいです.
これについて調べると SELinux が原因なのではないかという投稿をいくつか見かけましたが,今回は該当しません.

Dockerを問題から切り離すため,ホストで以下のように foo というファイルを作成したのち,fooのパーミッションを確認してみました.

% ls -ld .
drwxrwxr-x 7 myuser myuser 277 Apr 21 11:43 .
% chmod 777 . # root 権限で ファイルを作成できるようにする
% sudo touch ./foo
% ls -l ./foo
-rw-r--r-- 1 nobody nogroup 0 Jun 5 21:54 ./foo
% chmod 775 . # カレントディレクトリのパーミッションを元に戻す

その結果,foonobody:nogroup権限で作成されていることが判明しました.
つまり,rootユーザがroot:root権限のファイルを作成できていなかったのです.

これはどうやら,このディレクトリがマウントされていた NFS サーバの設定の影響だったようです.
研究室では,/home が NFS でマウントされていますが,他のユーザが他人のファイルなどを sudo 経由で閲覧などできないようにするために,このような設定を行っている,ということでした.
今まで考えたこともありませんでしたが,言われてみれば妥当だし必要な設定だと納得しました.

今回は,NFS でマウントされていないディレクトリで docker-to-linux を利用することでこの問題を解決しました.
他にも, Dockerfile 内でホストと同じディレクトリを作成して作業すると,解決できるかもしれません.

ハマった点2: イメージサイズ追加

QEMU でテストするために,ビルドに使った package やソースファイルをそのまま残しておいた結果, disk image のサイズがデフォルトの 1 GB では足りなくなってしまいました.
そこで,create_image.sh で disk image の初期化部分を

 echo_blue "[Create disk image]"
-dd if=/dev/zero of=/os/${DISTR}.img bs=$(expr 1024 \* 1024 \* 1024) count=1
+dd if=/dev/zero of=/os/${DISTR}.img bs=$(expr 1024 \* 1024 \* 1024) count=2

のように変更しました.

これに合わせ,partition.txtsize

-linux.img1 : start=2048, size=2095104, type=83, bootable
+linux.img1 : start=2048, size=4190208, type=83, bootable

と 2倍にしてみましたが,上手くいきません.
(ちなみに,このsize512 Bの sector が単位であり,size=2095104のときは合計で1023 MBとなります.)

partition.txt を使う sfdiskコマンドのmanページを見ると,sizeが省略された場合は最大限利用するということが分かったので,size=の部分を削除 しみてると,上手くいきました.
つまり,partition.txt を以下に変更すればOKです.

-linux.img1 : start=2048, size=2095104, type=83, bootable
+linux.img1 : start=2048, type=83, bootable

速度比較

docker-to-linux は Packer と比べて,桁違いに高速でした.

手法所要時間速度比
Packer7 min 29.3 s1 倍
Docker (ビルドキャッシュなし)49.4 s9.08 倍
Docker (ビルドキャッシュあり)6.6 s67.65 倍

Dockerのイメージを一から作る場合でも 9倍高速ですが,イメージが完全にキャッシュされているなら,たった 6.6秒 で完了する (67倍も高速),という結果になりました.
Docker はレイヤーごとにキャッシュしてくれるので,ビルドキャッシュが大部分は存在する,ということがほとんどです.
たった 6.6秒で disk image が作成できれば, disk image 自体の検証やその後のシミュレーションも捗りますね!

まとめ

今回は, Docker を使って gem5 FS モード用の disk image を作成する方法を紹介しました.
なお,gem5 で ベンチマーク Whisper を動かすための Dockerfile を追加した fork を公開しておりますので,参考になれば幸いです.

Footnotes

  1. リンク先1つ目の方法は out-dated とされているので除いています.