ソースコード・リテラシーのススメ

第23回 ソースコード・リテラシー【実践編 2】

この記事を読むのに必要な時間:およそ 7 分

Documentation/filesystems/ramfs-rootfs-initramfs.txtを見よ,とあるので,さっそくそのファイルを調べてみました。

% cat -n /usr/src/linux/Documentation/filesystems/ramfs-rootfs-initramfs.txt 
 1  ramfs, rootfs and initramfs
 2  October 17, 2005
 3  Rob Landley 
 4  =============================
 5  
 6  What is ramfs?
 7  --------------
 8  
 9  Ramfs is a very simple filesystem that exports Linux's disk caching
     ...

このファイルは以前,Plamoのインストーラをinitrd形式からinitramfs形式に変更する際に調べたことがありました。その際はinitramfs形式の作り方を中心に読んだのですが,今回は改めて頭から読み直しました。

このファイルでは,まずカーネルのディスクキャッシュの仕組みを用いたramfsというファイルシステムについて紹介し,従来使われていたram diskとの違いを解説しています。新しく実装されたramfsは,カーネルのキャッシュ機能を利用して動的に大きさが変更できるのに対して,従来のram diskではあらかじめメモリ上に決まった容量を割り当てなければならないので無駄が多いこと,ram diskの機能の多くはloopbackデバイスを使えば実現できるので時代遅れになっていることなどが説明されています。

そして,ramfsの特別な例としてのrootfsについて紹介されていました。

 70  What is rootfs?
 71  ---------------
 72  
 73  Rootfs is a special instance of ramfs (or tmpfs, if that's enabled), which is
 74  always present in 2.6 systems.  You can't unmount rootfs for approximately the
 75  same reason you can't kill the init process; rather than having special code
 76  to check for and handle an empty list, it's smaller and simpler for the kernel
 77  to just make sure certain lists can't become empty.
 78  
 79  Most systems just mount another filesystem over rootfs and ignore it.  The
 80  amount of space an empty instance of ramfs takes up is tiny.
 81  

rootfsはramfsで実現されたカーネル内蔵のファイルシステムで,カーネルがファイルシステムを探す際の起点として使うのでアンマウントはできず,カーネルの起動時パラメータで指定するroot=/dev/hda2等のHDD上の実際のルートパーティションは,このrootfsの上にマウントされることでルートファイルシステムと見なされるようです。

もう少し読み進めていくと,initramfsinitrdの違いについても紹介してありました。

114    - When switching another root device, initrd would pivot_root and then
115      umount the ramdisk.  But initramfs is rootfs: you can neither pivot_root
116      rootfs, nor unmount it.  Instead delete everything out of rootfs to
117      free up the space (find -xdev / -exec rm '{}' ';'), overmount rootfs
118      with the new root (cd /newmount; mount --move . /; chroot .), attach
119      stdin/stdout/stderr to the new /dev/console, and exec the new init.

以前,initramfsの作り方を中心に読んだ時にはこのあたりの話は読み飛していたのですが,改めて読み直してみるとinitrdとinitramfsの重要な違いが説明されています。

initrdの場合はメモリ上に割りあてたram diskをrootfs上にマウントしているので,pivot_rootすることで別のファイルシステムをrootfsにマウントし直すことができるのに対し,initramfsの場合はrootfsそのものにcpio+gzipで固めたファイルを書き込んでルートファイルシステムにしているため,データを消去することで使っていた領域を解放することはできるものの,pivot_rootでルートパーティションを切り替えることはできない,ということです。

実のところ,P-Plamoの参考にするためにSlaxの起動の仕組みなどを調べたことがあります。その際,Slaxでは従来のinitrd形式を使っているので,なぜ作りやすいinitramfs形式を使わないのだろう?と疑問を感じたのですが,終了時にCD/DVDメディアを排出するためには,pivot_rootで元のルートファイルシステムに戻る必要があり,そのためには従来のinitrd形式が必須だったようです。

実際にinitrd形式ならpivot_rootできるかどうかを試してみるために,initramfs形式で作っていた起動用ファイルシステムをinitrd形式に変更して試してみました。

# dd if=/dev/zero of=miniroot bs=1M count=4
4+0 records in
4+0 records out
4194304 bytes (4.2 MB) copied, 0.0188857 s, 222 MB/s
# mke2fs -i1024 -m0 miniroot
mke2fs 1.41.4 (27-Jan-2009)
initrd is not a block special device.
Proceed anyway? (y,n) y
...
# mount miniroot /loop -o loop
# ( cd ./initramfs ; tar cvf - * ) | ( cd /loop ; tar xf -)
bin/
bin/busybox
bin/sh
...

# umount /loop 
# gzip -c miniroot > initrd.gz

まずddコマンドでinitrd用に固定サイズのファイルをあらかじめ確保しておきます。この例では4Mバイト分,/dev/zeroから0を書きこんだファイル(miniroot)を作りました。

そのファイルをmke2fsでフォーマットし,/loopにloopback形式でマウントそこにinitramfs用に作っていたファイル一式をtarコマンドを使ってコピーし,念のためumountしてからgzipで固めてinitrd.gzを作りました。

このinitrd.gzに入れ替えて作成したDVDイメージで起動し,pivot_rootの動作を試しました。

図1 pivot_rootを実行して/loopを新しいルートファイルシステムに切り替え

図1 pivot_rootを実行して/loopを新しいルートファイルシステムに切り替え

起動直後はinitrdが/dev/rootになり,DVDメディア上のLiveLinux用のルートファイルシステムは/loop以下に配置されているのに対し,/loop ディレクトリでpivot_root . put_oldした後は,/loop/tmp/tmpになる等,LiveLinux 用のルートファイルシステムが本来の位置に配置され,元のinitrd上のファイルシステムはput_oldディレクトリ以下に移動しています。

この状態で,再度put_old以下のディレクトリに移動してpivot_rootを実行すれば,無事,元のinitrd上のファイルシステムがルートファイルシステムに戻りました。

/ # cd /put_old
/put_old # /sbin/pivot_root . put_old
/ # ls /
bin         etc         loop         proc         shutdown
cdrom       init        lost_found   put_old      sys
dev         lib         new_root     sbin

pivot_rootでルートファイルシステムを行き来できるメドがついたので,最終的な終了処理はinitrd上のshutdownコマンドで行い,そこからejectコマンドを実行するようにして,何とか終了時にDVDメディアを排出できるようになりました。

今回はinitramfsでpivot_rootできない原因をカーネルのソースコードまで辿って調べてみましたが,実際の処理そのものを調べるまでもなく,コメントと付属のドキュメントで原因は判明しました。

前回のpmountもそうでしたが,ソースコードを読む,といっても,アルゴリズムや処理のステップを丁寧に追うまでもなく,デバッグメッセージやコメント行を調べる程度で解決する問題は多数あります。ソースコードや付属ドキュメントを「難しそうだから」⁠英語だから」と敬遠せず,本連載で紹介してきたように,機会を見つけてはチェックするようにすることが,問題解決の勘を養い,初心者から一歩を踏み出す近道になるでしょう。


さて,春は出会いと別れの季節です。約2年ほど続いたこの連載も,カーネルソースまで辿りついた今回で一段落させてもらうことになりました。ご愛読を感謝すると共に,これからも皆さんがソースコードやドキュメントに親しんでいかれることを願って,終了の挨拶とさせていただきます。長い間,ありがとうございました。

著者プロフィール

こじまみつひろ

Plamo Linuxとりまとめ役。もともとは人類学的にハッカー文化を研究しようとしていたのが,いつの間にかミイラ取りがミイラになってOSSを仕事にするようになってしまいました。最近はスペシャリスト養成を目的とした専門職大学院で教壇に立ったりもしています。

URLhttp://www.linet.gr.jp/~kojima/Plamo/index.html