過去数回に渡って最近のLinux起動の仕組みを解説し、その際に実行されるスクリプトや参照される設定ファイルなどを眺めてきました。実のところ、このあたりのシステムの基盤部分に関する設定は、ヘタにいじるとシステムが正しく起動しなくなることもあるため、一般ユーザはおろかシステム管理者でも通常は気にする必要のない部分だったりします。
しかしながら、筆者のようにディストリビューションの開発にたずさわっていると、このような基盤部分のトラブルにもしばしば直面します。
基盤部分のトラブルは、ディストリビューションの設計方針などとも関係するため、Google等で調べても直接解決することは少なく、付属ドキュメントや設定ファイル、ソースコードなどを総合的に検討する必要があります。
今回は、トラブル時の調査や分析の一例として、Plamo-4.5にext4ファイルシステムを組み込む際に遭遇したトラブルを用いて、それらトラブルの原因をどのように調べ、解決したかの例を紹介してみようと思います。
e2fsprogs-1.41 の導入
筆者がとりまとめ役をしているPlamo Linuxでは、ルートパーティションのファイルシステム(fs)として ext2/ext3/reiser の3種を使えるようにしていましたが、最近ではreiser fsの将来性が怪しくなると共に、ext4 fsも実用レベルになってきたようなので、ext4 fsに対応するための作業を始めました。
ext4 fsとはext2/ext3 fsの開発者たちが開発している新しいファイルシステムで、少々時代遅れの感のある ext2 の基本設計に、 extentsやブロック番号の48ビット化、ディレクトリインデックスのハッシュテーブル化、タイムスタンプの高精度化など、さまざまな新機能を追加したファイルシステムです。
まず、ext4 fsを作成するためのツールであるe2fsprogs の最新版である1.41をビルドし、インストーラに組み込みました。また、インストール用のスクリプトでもext4をファイルシステムとして選択できるように修正しました。
この状態でテストしてみると、mkfs.ext4コマンドは正しく実行されるものの、パッケージをインストールしている途中で “ OOM killer” が発生して、異常終了してしまいます。
図1 OOM killerが発生!
“OOM(Out Of Memmory) killer” とは、カーネルが使用可能なメモリを使い尽してしまい、動作中のプロセスをランダムに終了させる現象です。“ OOM killer” は本来起きてはならない現象で、プロセス管理やメモリ管理が見直された最近のLinuxではまず発生しないはずです。
当初は「使っているカーネルにext4の機能を組み込み忘れたのが敗因だろう」と考えて、カーネルを最新版に更新してext4の機能を組み込むことにしました。Plamo-4.22で使っていたカーネルは2.6.24系でしたが、最近では2.6.26系が公開されているので、その最新バージョンである 2.6.26.1 を採用し、make menuconfig のファイルシステムの設定で Ext4dev/ext4 を有効にしたカーネルを作成、パッケージ化すると共に、インストーラのカーネルも更新しました。
再度テストしたところ、やはりパッケージをインストールする段階でOOM Killerが発生します。「 これはおかしい」と、インストーラの各段階ごとにファイルシステムの状況やプロセス、メモリの使用状況などを確認しながら進めたところ、ext4 形式で作成したルートパーティションがマウントできていない ようです。
インストーラのシェルを使って対話的に操作してもやはりエラーになってマウントできません。
# mkfs.ext4 /dev/sda1
mke2fs 1.41.0 (10-Jul-2008)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
...
Thie filesystem will be automatically checked every 37 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.
# mount /dev/sda1 /mnt
mount: wrong fs type, bad option, bad superblock on /dev/sda1,
missing codepage or other error
In some cases useful info is found in syslog - try
dmesg | tail or so
エラーメッセージにあるようにdmesgの内容を調べてみたところ、“ unsupported optional features” なるエラーが報告されています。
# dmesg | tail
....
Unikey successfully installed.
Ext3-fs: sda1: couldn't mount because of unsupported optional features (240).
「あれれ、なぜExt3-fsのエラーなんだろう?」と思って、カーネルがサポートしているファイルシステムの一覧を確認するために/proc/filesystems を調べました。
# cat /proc/filesystems
nodev sysfs
nodev rootfs
...
nodev devpts
reiserfs
ext3
ext4dev
ext2
nodev ramfs
...
この結果を見ると、カーネルがサポートしているファイルシステムはext4dev であってext4 ではないようです。確かにe2fsprog-1.41.0にはmkfs.ext4 とmkfs.ext4dev の2種のコマンドがあります。「 ext4とext4devって異なるファイルシステムという扱いなのかな?」と首をかしげつつ、こういった場合はmenuconfigのヘルプメッセージ が参考になるだろうと、make menuconfig を実行して、“ Ext4dev/ext4 extended fs support development” のHelpメッセージを読んでみました。
図2 menuconfigのext4devに関するヘルプメッセージ
このヘルプメッセージを見る限り、ext4 fsは現在も開発が進行中の機能で、開発が完了するまではext4devという名称にしてあるようです。
それでは、mkfs.ext4とmkfs.ext4devの違いはどこにあるのでしょう? e2fsprogsと共にインストールされる、これらのコマンドの設定ファイル/etc/mke2fs.conf を調べてみました。
# cat /etc/mke2fs.conf
[defaults]
base_features = sparse_super,filetype,resize_inode,ext_attr
blocksize = 4096
inode_size = 256
inode_ratio = 16384
[fs_types]
ext3 = {
features = has_journal
}
ext4 = {
features = has_journal,extents,huge_file,flex_bg,uninit_bg,dir_nlink,extra_isize
inode_size = 256
}
ext4dev = {
features = has_journal,extents,huge_file,flex_bg,uninit_bg,dir_nlink,extra_isize
inode_size = 256
options = test_fs=1
}
small = {
blocksize = 1024
inode_size = 128
inode_ratio = 4096
}
...
この設定ファイルは、それぞれの形式でファイルシステムを作成する際に利用するオプション類を指定しています。このファイルを見る限り、ext4とext4devの違いは、test_fs=1 というオプションを指定するかどうかだけのようです。このオプションを指定すると何が変わるのだろう、と mke2fsのmanページ を調べてみました。
# export LANG=C ; man mke2fs
MKE2FS(8) MKE2FS(8)
NAME
mke2fs - create an ext2/ext3 filesystem
...
test_fs
Set a flag in the filesystem superblock indicating that it
may be mounted using experimental kernel code, such as the
ext4dev filesystem.
manページを見ると、test_fsというオプションは-Eで指定する拡張オプションの1つで、ファイルシステムのスーバーブロックに開発中のコードを使うことを示すフラグを立てる指定のようです。
man mke2fsの前にexport LANG=C しているのは、日本語版ではなく英語版のmanページを参照するためです。日本語版のmanページは便利ですが、新しいバージョンが翻訳されるまで時間がかかるので、ext4のような最新機能を調べるにはソースコードに付属の英語版のmanページを参照する必要があります。
「なるほど」と思って、mkfs.ext4dev コマンドで再度ファイルシステムを作成し、Ext4dev/ext4機能を組み込みにしたカーネルでマウントできるか試してみました。
# mount /dev/sda2 /mnt -t ext4dev
EXT4-fs: sda2: Filesystem with huge files cannot be mounted read-write without CONFIG_LSF.
mount: wrong fs type, bad option, bad superblock on /dev/sda2,
missing codepage or helper program, or other error
In some cases useful info is found in syslog - try
dmesg | tail or so
うーん、まだダメのようです。dmesgを調べても、“ EXT4-fs: sda2:...” という同じメッセージが出力されているだけです。
カーネルの設定オプションの調査
CONFIG_LSF というのはカーネルをコンパイルする際のオプション指定のようなので、カーネルのビルドオプション設定ファイルである/usr/src/linux/.config を調べました。下記でgrepに与えている-C 3オプションは、該当する行の前後3行を合わせて表示するという指定です。
# grep -C 3 CONFIG_LSF /usr/src/linux/.config
CONFIG_BLOCK=y
CONFIG_LBD=y
# CONFIG_BLK_DEV_IO_TRACE is not set
# CONFIG_LSF is not set
CONFIG_BLK_DEV_BSG=y
なるほど、確かにCONFIG_LSF という設定はコメントアウトされて有効になっていません。このオプションはどこで指定するのだろう…、とmenuconfigの画面をあちこち探し回って、ようやく、Enable the block layer -> Support for Large Single Files というオプションを発見しました。解説によると、2Tバイトを越えるような巨大なファイルを扱いたい時に指定するオプションだそうです。そんな巨大なファイルを扱う必要はないだろうと考えて、Plamo-4.5用のカーネル設定ではオフにしていた機能ですが、ext4 fsのヘルプメッセージにあったように、ext4 fsは16Tバイトを越えるようなファイルを扱えるように設計されているため、この機能を必要とするのでしょう。
改めてmake menuconfig でこのオプションを有効にした上でカーネルを再構築したところ、ようやくext4devファイルシステムが使えるようになりました。
# mount /dev/sda2 /mnt -t ext4dev
# dmesg | tail
...
EXT4 FS on sda2, internal journal
EXT4-fs: mounted filesystem with ordered data mode.
EXT4-fs: file extents enabled
EXT4-fs: mballoc enabled
なお、インストーラでOOM killerが発生していたのは、インストール用のパーティションがマウントできなかったため、 ramdisk上のmntディレクトリ以下にパッケージが展開されてしまい、ramdiskがメモリを使い果たしたせいでした。最新のカーネルといえども、利用可能なメモリがどんどん少なくなっていくという異常な状況は如何ともしがたかったようです。
grubの更新
上記の作業でext4 fsがルートファイルシステムの一種として利用可能になったものの、実際にext4 fsをルートファイルシステムに指定してインストールテストを行うと、インストールは無事終了するものの、再起動しようとするとgrubが正しく起動しません。
当初は「grubのインストールをミスったのかな?」と思って、インストーラのgrubからHDD上のカーネルを読み込ませようとしましたが、インストーラのgrubもファイルシステムの種類こそ ext2fs だと認識するものの、“ Bad file or directory type” 言ってファイルシステムの中身を読んでくれません。
図3 grubのエラーメッセージ
一方、インストーラのシェルを操作して、手動でこのパーティションをマウントすることは可能です。
「どうもgrubがext4に対応していないみたいだ」と考えて、Googleでext4とgrubをキーワードに検索してみると、grub legacy(grub-0.97) はext4 fsで追加されたextents機能に対応していないこと、対応するためのパッチがすでにgrub開発者のメーリングリスト(bug-grub@gnu.org)に投稿されていることなどがわかりました。
かってLinux上で広く使われていたブートローダであるliloでは、ロードすべきカーネルイメージをHDDの絶対番地(CHSやLBA)で指定していました。そのため、カーネルを更新するたびに/sbin/liloコマンドを再実行し、カーネルイメージの絶対番地を再登録してやる必要がありました。一方、grubではカーネルイメージはファイル名で指定できて便利なのですが、そのためにはgrub自身がカーネルイメージを置いたファイルシステムの構造を理解する必要があり、ext4 fsのような新しい構造のファイルシステムに対応するにはそのためのコードが必要となります。
本文中では触れませんでしたが、今回のトラブルでもliloは問題なくext4 fs上のカーネルを起動することができたので、liloのシンプルさは捨てたものじゃないな、と改めて感心しました。
パッチがあれば話は簡単、とブラウザからカット&ペーストしてパッチファイルを作り、ext4 fsに対応するためのパッチを適用しようとしましたが、画面を見易くするために長い行はブラウザが自動的に改行コードを挿入してくれるのか、カット&ペーストのままではあちこちでパッチファイルの不整合が生じています。
それでは、とソースコードをダウンロードして使おうとしても&等のHTMLのスペシャルキャラクタが邪魔をしてやはり正しく適用されません。
結局、パッチファイルを手で整形した上で、適用しそこねた部分は手動で修正し、再度差分を取り直すことで、何とか ext4 対応の grub をパッケージ化できました。作成した新しいgrubパッケージを使えば、TABキーを叩くとext4 fs上のファイル名もきちんと補完してくれました。
このgrubパッケージをPlamo-4.5のツリーに移して、ようやくext4 fsをルートファイルシステムとして使えるようになりました。
今回の例ではインストーラでの動作チェックやマウント状況の確認、grubでの動作チェックなど、段階を踏んで進めることができたため、それぞれの段階で発生した問題を比較的容易に解決できましたが、このトラブルが「ext4 fs 上にインストールすると起動しない」といった形で重合して発生した場合、要因の切り分けや原因の特定にはもっと時間がかかったことでしょう。トラブル解決の際には少々遠回りに見えても、ひとつひとつ条件を確認した上で進む方が、結局はより速く目的地に到着することがよくあります。