前回 紹介したように、P-Plamo はHDDにインストールしなくても、DVDから直接起動して利用することができるPlamo Linux版のLiveDVDです。
インストール作業が不要なLiveDVDは便利なツールですが、元々UNIX/Linux環境は読み書きが自由にできるHDDにインストールして使うことが前提になっているので、書き込み不可なDVDメディアとはあまり相性がよくありません。
もちろん、オープンソースであるLinuxの場合、組み込み環境向けのディストリビューションのように、HDDのない環境で動作するように改造することも不可能ではありませんが、わざわざLiveDVD用に専用のディストリビューションを作るのも大変な作業です。
そこで既存の環境を流用しながら、どうやってうまくDVDから起動して運用できるようにするかにLiveDVDの作者は頭を絞ることになります。
幸い、linuxには今回紹介するinitrd をはじめ、ファイルをファイルシステムとしてマウントするループバックファイルシステム や、さまざまな圧縮ファイルシステム 、自動的に増減するtmpfs といったトリッキーな(?)機能が豊富に用意されているので、これらを組み合わせて使いやすい機能をどのように実現するかが作者の腕の見せ所です。
P-Plamo DVDの中身
通常、OSを動かすためには数千を越えるファイルが必要になりますが、P-PlamoのようなLiveDVDの場合、実際のファイルはsquashfs上に圧縮されているので、DVDの中身はごくわずかのファイルしかありません。
DVDをマウントして中身を見てみると、以下のようなファイルだけになっています。
% ls -lRh /cdrom
/cdrom:
合計 6.1M
-r--r--r-- 1 root root 5.3K 3月 29日 15:19 ChangeLog
-r--r--r-- 1 root root 6.0M 3月 28日 01:47 initrd
dr-xr-xr-x 2 root root 2.0K 3月 29日 10:19 isolinux/
/cdrom/isolinux:
合計 1.6G
-r--r--r-- 1 root root 2.0K 3月 29日 15:21 boot.cat
-r--r--r-- 1 root root 2.3M 3月 29日 15:21 initrd.gz
-r--r--r-- 1 root root 14K 3月 29日 10:57 isolinux.bin
-r--r--r-- 1 root root 658 3月 29日 15:20 isolinux.cfg
-r--r--r-- 1 root root 120K 2月 6日 21:00 plamo41.lss
-r--r--r-- 1 root root 4.1K 2月 14日 14:28 pplamo.lss
-r-xr-xr-x 1 root root 1.6G 3月 28日 02:46 rootimg.squash*
-r--r--r-- 1 root root 464 3月 28日 11:16 sample.msg
-r--r--r-- 1 root root 738 3月 29日 15:20 syslinux.cfg
-r--r--r-- 2 root root 2.2M 3月 29日 10:19 vmlinuz
-r--r--r-- 2 root root 2.2M 3月 29日 10:19 vmlinuz-2.6.32.10-plamoSMP
これらのファイルのうち、P-Plamoに必要なのはisolinuxディレクトリ以下のファイルで、DVDのルートディレクトリにあるChangeLog は変更履歴の簡単なメモ、initrd は後述する起動時ramdiskの圧縮前のイメージで、参照用に置いているのでP-Plamoの動作には不要です。
また、isolinuxディレクトリ以下のファイルでも、pplamo.lss (イジりかけの起動画面イメージ)とsyslinux.cfg (USBブート用の設定ファイル)はDVD起動の場合は利用されませんので、この2つのファイルを除いた残りのファイルについて簡単に説明しましょう。
boot.cat
CD/DVDからPCを起動するために提案されたEl Torito規格 が使う起動イメージのカタログファイルです。El Torito規格では、1枚のCD/DVDから複数のOSを起動することが想定されており、それぞれのOSのブート用の領域がどこにあたるかを記録するためにこのファイルが利用されます。
initrd.gz
圧縮した起動用ramdisk ファイル。カーネルとともにメモリ上に読み込まれ、起動用のルートファイルシステムとして利用されます(詳細は後述) 。
isolinux.bin
isolinux と呼ばれるCD/DVD起動用のブートローダです。次のisolinux.cfgの設定に従って、カーネルや起動用ramdiskをメモリに読み込みます。
isolinux.cfg
isolinux.binが参照する設定ファイルで、読み込むべきカーネルや起動用ramdisk、カーネルに与えるオプションパラメータ等を設定します。
plamo41.lss
DVD起動時の背景に表示される画像データファイルです。syslinux/isolinux専用の、lssと呼ばれるシンプルだけどやや特殊な画像形式になっています。
rootimg.squash
squashfs化したPlamo-4.72のファイルシステムをLZMA形式で圧縮したファイルです。このファイルがP-Plamoのルートファイルシステム になります。
sample.msg
起動時に表示されるメッセージを記述したファイルで、指定可能なラベルやログイン用の情報などを記しています。
vmlinuz、vmlinuz-2.6.32.10-plamoSMP
linuxカーネル。vmlinuz-2.6.32.10-plamoSMP が本来の名称ですが、El Torito規格を用いてCD/DVDから起動する際には、長いファイル名を格納するロックリッジ拡張機能が利用できないため、vmlinuz-2.6.32.10-plamoSMPからvmlinuzという名前にリンク(ハードリンク)を張って、短いファイル名で参照 できるようにしています。
これらのファイルのうち、isolinux.cfgとsample.msgがテキストファイル、その他はバイナリファイルになっています。
P-Plamo用initrd
initrd は起動用ramdisk(INITial RamDisk)の謂で、ブートローダによってカーネルと共にメモリ上に読み込まれ、カーネルがHDD上にある本来のルートファイルシステムを読み込む際に必要な準備作業(ドライバを読み込んだり、RAID/LVMを設定したり)を行うための小さなファイルシステムです。
initrdはカーネルのモジュール化が始まったlinux-2.0のころから採用されている機能です。ブロックデバイスやファイルシステムのドライバもモジュール化されるようになった最近のカーネルでは、そのままでは「HDDを読むためのドライバはHDDの中 」状態になってしまうので、たいていのディストリビューションではHDDを読むために必要なドライバ類は、initrdを使って起動時にカーネルに組み込むようになっています。
Plamo Linuxでは、ブロックデバイスやファイルシステム用のドライバはカーネル組み込みにしてしまう方がカーネルの再構築や更新が簡単だと考えているので、現時点ではinitrdは採用していませんが、DVD上のsquashfsをルートファイルシステムに見せるような処理にはinitrdが必須なので、P-Plamoでは独自のinitrdを用意して必要な機能を実現しています。
先ほどは「必要なドライバ類を事前にinitrdを使ってカーネルに組み込む」と書きましたが、initrdが提供するのは単なるファイルシステムであり、その上にドライバモジュール類を置けば自動的に組み込んでくれるわけではありません。ドライバをカーネルに組み込むためには専用のコマンドが必要ですし、どのようなドライバをどういう順番に組み込むかといった制御処理にはシェルスクリプトのような機能も必要になります。
また、モジュールドライバを組み込むためにはカーネル内部の情報を利用するためのproc fsやsysfsといった仮想ファイルシステムも必要になりますし、周辺機器を操作するにはデバイスファイルも必要です。すなわちinitrdは小さなファイルシステムではあるものの、必要なコマンドやディレクトリ構造を備えた完結した1つのLinux環境 です。
P-Plamoで使っているinitrdは、前節で紹介したように非圧縮の状態でDVDのルートディレクトリにも置いているので ループバック機能を使ってマウントすれば簡単に中身を見ることができます。
# mount -o loop /cdrom/initrd /loop
# ls -l /loop
合計 32,768
drwxr-xr-x 2 root root 1,024 3月 29日 00:07 bin/
drwxr-xr-x 2 root root 1,024 2月 3日 2009年 cdrom/
drwxr-xr-x 6 root root 3,072 2月 21日 21:58 dev/
drwxr-xr-x 2 root root 1,024 3月 29日 13:40 etc/
-rwxr-xr-x 1 root root 5,380 3月 29日 15:15 init*
drwxr-xr-x 4 root root 1,024 3月 5日 23:48 lib/
drwxr-xr-x 2 root root 1,024 2月 3日 2009年 loop/
drwx------ 2 root root 12,288 2月 3日 2009年 lost+found/
drwxr-xr-x 2 root root 1,024 2月 4日 2009年 new_root/
drwxr-xr-x 2 root root 1,024 2月 3日 2009年 proc/
drwxr-xr-x 2 root root 1,024 2月 3日 2009年 put_old/
drwxr-xr-x 2 root root 1,024 3月 6日 2009年 sbin/
-rwxr-xr-x 1 root root 620 3月 5日 23:40 shutdown*
drwxr-xr-x 2 root root 1,024 2月 27日 2009年 sys/
P-Plamoのinitrdではbusybox を使ってモジュールの組み込みやスクリプト処理を行っています。busybox は組み込み環境向けに開発されている「十徳ナイフ(Swiss Army Knife) 」のようなツールで、cpやmv、lsをはじめとして、よく使われるUNIX/Linuxの基本コマンドをサイズ重視の方針で再実装し、約1.2Mバイトほどのサイズのバイナリに230ほどのコマンドを詰め込んでいます。
% /loop/bin/busybox
BusyBox v1.13.2 (2009-02-03 20:32:13 JST) multi-call binary
Copyright (C) 1998-2008 Erik Andersen, Rob Landley, Denys Vlasenko
and others. Licensed under GPLv2.
See source distribution for full notice.
Usage: busybox [function] [arguments]...
or: function [arguments]...
BusyBox is a multi-call binary that combines many common Unix
utilities into a single executable. Most people will create a
link to busybox for each function they wish to use and BusyBox
will act like whatever it was invoked as!
Currently defined functions:
[, [[, addgroup, adduser, adjtimex, ar, ash, awk, basename, blkid, bunzip2, bzcat, bzip2, cal, cat, catv, chattr,
chgrp, chmod, chown, chpasswd, chroot, chrt, chvt, cksum, clear, cmp, comm, cp, cpio, cryptpw, cut, date, dd,
deallocvt, delgroup, deluser, devmem, df, diff, dirname, dmesg, dos2unix, du, dumpkmap, echo, ed, egrep, eject,
env, expand, expr, false, fbset, fbsplash, fdflush, fdformat, fdisk, fgrep, find, findfs, fold, free, freeramdisk,
fsck, fsck.minix, ftpget, ftpput, fuser, getopt, getty, grep, gunzip, gzip, halt, hd, hdparm, head, hexdump, hostid,
hostname, hwclock, id, ifconfig, init, insmod, install, ip, ipaddr, ipcalc, ipcrm, ipcs, iplink, iproute, iprule,
iptunnel, kbd_mode, kill, killall, killall5, klogd, length, less, linux32, linux64, linuxrc, ln, loadfont, loadkmap,
logger, login, logname, logread, losetup, ls, lsattr, lsmod, lzmacat, makedevs, md5sum, mdev, mesg, mkdir, mkfifo,
mkfs.minix, mknod, mkswap, mktemp, modprobe, more, mount, mountpoint, mv, netstat, nice, nohup, nslookup, od,
openvt, passwd, patch, pidof, ping, pipe_progress, pivot_root, pkill, poweroff, printenv, printf, ps, pwd, raidautorun,
rdate, rdev, readahead, readlink, readprofile, realpath, reboot, reset, resize, rm, rmdir, rmmod, route, rtcwake,
run-parts, runlevel, script, sed, seq, setarch, setconsole, setfont, setkeycodes, setlogcons, setsid, sh, sha1sum,
showkey, sleep, sort, split, start-stop-daemon, stat, strings, stty, su, sulogin, sum, swapoff, swapon, switch_root,
sync, syslogd, tac, tail, tar, tee, telnet, test, time, top, touch, tr, true, tty, ttysize, udhcpc, umount, uname,
uncompress, unexpand, uniq, unix2dos, unlzma, unzip, uptime, usleep, uudecode, uuencode, vi, vlock, wc, wget,
which, who, whoami, xargs, yes, zcat
この例に見られるように、busyboxには[ (スクリプト中で各種条件をテストするコマンド)やaddgroup をはじめ、vi やwget といった機能(function)が1つのバイナリに詰め込まれ、それぞれの機能は引数として指定するか、各機能名のシンボリックリンク経由でbusyboxを呼び出して使うようになっています。
% ls -lh /loop/bin
合計 1.2M
-rwxr-xr-x 2 root root 1.2M 2月 4日 2009年 busybox*
lrwxrwxrwx 1 root root 7 3月 28日 01:49 cat -> busybox*
lrwxrwxrwx 1 root root 7 3月 29日 00:07 chmod -> busybox*
lrwxrwxrwx 1 root root 7 3月 28日 01:49 chroot -> busybox*
lrwxrwxrwx 1 root root 7 3月 28日 01:49 cp -> busybox*
lrwxrwxrwx 1 root root 7 3月 28日 01:49 cpio -> busybox*
...
このシンボリックリンクの設定により、/loop/bin/catを実行すれば、/loop/bin/busyboxをcat機能を指定して実行したものと見なされます。
busyboxはこのように便利なツールではありますが、サイズ重視で再実装しているため、各機能のオプション指定には制限があり、普段使っている便利なオプションが使えないこともよくあります。
Plamo Linux標準のcat コマンドは、GNU coreutils由来で、以下のような豊富なオプションを誇ります。
% /bin/cat --help
使用法: /bin/cat [オプション]... [ファイル]...
Concatenate FILE(s), or standard input, to standard output.
-A, --show-all equivalent to -vET
-b, --number-nonblank number nonempty output lines
-e equivalent to -vE
-E, --show-ends display $ at end of each line
-n, --number number all output lines
..
一方、busyboxのcat には-u というオプションしかありませんし、しかもそのオプションは無視されるそうです。
% /loop/bin/cat --help
BusyBox v1.13.2 (2009-02-03 20:32:13 JST) multi-call binary
Usage: cat [-u] [FILE]...
Concatenate FILE(s) and print them to stdout
Options:
-u Use unbuffered i/o (ignored)
busyboxのストイックな設計は、GNUソフトウェアの豊富すぎる機能に慣れた身には戸惑うこともよくありますが、単機能コマンドを組み合わせて必要な処理を実現するのはパズル解き的な快感がありますし、これだけの機能を詰め込んだバイナリが、静的リンクでも1.2Mバイト程度に収まっているのは驚異的であると同時に、きわめて魅力的です。
P-Plamoのinitrdは、このbusyboxのシェル(bashの元になったBourne shell互換のシェル)を使った/initシェルスクリプトを実行してLiveDVDに必要な初期化処理を実現しています。
initrdの初期化処理(その1)
initrdは、isolinux.cfg の設定に従ってカーネルと共にブートローダ(isolinux.bin)によってメモリ上に読み込まれ、isolinux.cfgのinit=/init の指定によって、自らの初期化処理を終えたカーネルは/initスクリプトを起動し、以後の処理を委ねます。
上述のように/initコマンドはsh互換のシェルスクリプトになっていますので、その前半部分を紹介しましょう。
1 #!/bin/sh
2 export PATH=/bin:.
3
4 mount -t proc proc /proc
5 mount -t sysfs sys /sys
まずPATHをbusyboxを収めている/binに設定し、次にカーネルの内部情報を利用するためのproc fsやsysfsをマウント しています。ここで使っているmountコマンドも、その実体はbusyboxです。
6
7 for i in exportfs.ko aufs.ko squashfs.ko ; do
8 insmod /lib/modules/$i
9 done
10
11 # nls_euc-jp.ko needs vfat.ko
12 for i in isofs.ko nls_iso8859-1.ko nls_ascii.ko nls_cp932.ko cdrom.ko loop.ko sr_mod.ko sg.ko fat.ko vfat.ko nls_euc-jp.ko ; do
13 insmod /lib/modules/$i
14 done
15
16 # for keyboard operation
17 for i in libps2.ko atkbd.ko hid.ko usbhid.ko ; do
18 insmod /lib/modules/$i
19 done
これらはカーネルに各種モジュールドライバを組み込む作業 です。P-PlamoではPlamo-4.72と同等のカーネルを使っているので、squashfsのドライバやloopbackファイルシステム、CD/DVDのファイルシステム用のドライバなどもモジュールになっているため、必要なドライバは事前にinitrd上でカーネルに組み込んでおかないと、DVDをマウントしたり、DVD上のファイルをループバック機能を用いてマウントすることができません。また、後述するシェルに落ちた場合に操作できるように、キーボード用のドライバも読み込ませています(17行目) 。
これらのモジュールはinitrd上の/lib/modules ディレクトリ に格納されています。
20
21 mount_ok=0
22
23 # check USB device first
24 if [ $mount_ok -eq 0 ]; then
25 sleep 5
26 echo -n "trying USB device:"
27 for i in sda sdb sdc sdd sde sdf sdg sdh ; do
28 if [ -d /sys/block/$i ]; then
29 removable=`cat /sys/block/$i/removable`
30 if [ $removable -eq 1 ]; then
31 echo -n "$i "
32 partition="$i"1
33 mount /dev/$partition /cdrom 2> /dev/null
34 if [ $? = "0" ]; then
35 if [ -f /cdrom/isolinux/rootimg.squash ]; then
36 echo "found P-Plamo image on /dev/$partition"
37 CD_DEV="/dev/$partition"
38 mount_ok=1
39 break
40 else
41 umount /cdrom
42 fi
43 fi
44 fi
45 fi
46 done
47 fi
最近のP-PlamoはUSBメモリからブートしても使えるようにしており、起動時にはまずUSBメモリ上に必要なファイルがあるか を調べています。
USBメモリはSCSIデバイスとして認識されるので、27行目からのループで、sdaからsdhまでの SCSIデバイスを対象に、sysfs上にその名前のブロックデバイスが存在するか を調べて(28行目) 、もしあればそのデバイスがリムーバブルデバイスか をチェックして(29行目) 、リムーバブルデバイスならばP-Plamoの入ったUSBメモリの可能性があるので、そのデバイスの最初のパーティションをinitrd上の/cdromにマウント してisolinux/rootimg.squashファイルが存在するかをチェックしています(35行目) 。
マウントしたUSBメモリ上にisolinux/rootimg.squashファイルが見つかればマウント処理は終了しますが、見つからなかった場合はumountして(41行目) 、次のデバイスをチェックします。
USBメモリ上にisolinux/squashfs.imgファイルが見つからなければ、次にDVDメディアのチェックに移ります。DVDメディアはUSBメモリよりも待ち時間を長めにしないといけないので、チェックは後回しにしています。
48
49 # check CD device
50 if [ $mount_ok -eq 0 ]; then
51 sleep 10
52 echo -n "trying CD device:"
53 for i in sr0 sr1 sr2 ; do
54 if [ -d /sys/block/$i ]; then
55 echo -n "$i "
56 mount -t iso9660 /dev/$i /cdrom -o ro 2> /dev/null
57 sleep 3
58 if [ $? = "0" ]; then
59 if [ -f /cdrom/isolinux/rootimg.squash ]; then
60 echo "found P-Plamo image on /dev/$i"
61 CD_DEV="/dev/$i"
62 mount_ok=1
63 break
64 else
65 umount /cdrom
66 fi
67 fi
68 fi
69 done
70 fi
最近のPlamo Linuxでは、従来のATAドライバを廃して、ATAデバイスも新しいlibata 経由でアクセスするようにしているので、/dev/hdc等のATAデバイスに接続されたDVDドライブもSCSIのCD/DVDドライブである/dev/sr0 等に見えます。
53行目にあるように、最初から3つのディスクドライブ(/dev/sr[012])について、USBメモリの時と同様、sysfsでブロックデバイスとして認識できるデバイスをマウントしてisolinux/rootimg.squashの有無をチェックしています。
71
72 # cannot find any CD device
73 if [ $mount_ok -eq 0 ]; then
74 echo "cannot find partition include squashfs. cannot continue booting. exit"
75 exec /bin/sh
76 fi
USBメモリやDVD上にisolinux/rootimg.squash が見つからなければ、その旨のメッセージを出力し(74行目) 、initrd 上のシェル(/bin/sh 実体はbusybox)に後を任せてスクリプトを終了します(75行目) 。この状態でシェルに落ちるということは何か致命的な問題が生じているのですが、先にキーボード用のモジュールドライバも組み込んでいるので、問題が何かはbusyboxを対話的に操作して調べることができます。
実のところ、Red Hat系のinitrdが使っているnash のような専用コマンドではなく、SuSE系のように処理に必要なコマンドのみをlibcと共にinitrdに組み込む形式でもなく、P-Plamoのinitrdにbusyboxを使っているのは、このようにトラブった場合にシェルが使えて対話的な操作でトラブルの原因を追求できるからです。
この後、initの処理はsquashfsをルートファイルシステムにするための処理に移りますが、だいぶ長くなったので、それらは次回に譲りましょう。