[English] [Japanese]

Linux CD-ROM ゲームシステム

 
  YAMAMORI Takenori ●yamamori@kt.rim.or.jp

initrd-game.imgの作成

前述の流れ図 に示した通り、CD-ROMゲームシステムは initrd を巧妙に使ってブートする。そこで、このゲームシステム用の initrd である initrd-game.img の作成方法を以下に説明する。


cdmount.cの作成

システムのブート時には、initrd-game が仮の“/”として最初にマウントされる。 しかしこの中には、/bin/shなど、ごく一部のコマンドしかなく、 lsコマンドすらない(ここではlsの代わりにecho * とやるのが常套手段)。 そして、この環境上でCD-ROMをマウントしなければならないのだが、 実は、initrd-game の中には mount コマンドもないのだ。

だからといって、通常のLinux上の mount コマンドを initrd-game 内にコピーしてはいけない。なぜなら、通常のmount コマンドは libc と動的リンクされているため、libc も必要になるからだ。 そして、libcはサイズが大きいため、initrd-game に入れたとしても、それがあとでブートFDに入らなくなるのだ。

そこで、静的リンクされた単機能の特別なmountコマンドを 以下のようにC言語で作成する。ファイル名はcdmount.cとした。

●リスト cdmount.c
#include <sys/mount.h>

main(int argc, char **argv)
{
  int data = 0;
  return mount(argv[1], argv[2], argv[3], MS_RDONLY|MS_MGC_VAL, &data);
}

cdmount.cのコンパイルは、必ず以下のようにオプションを付けて行なう。

gcc -O2 -static -s -o /tmp/cdmount cdmount.c

静的リンクのバイナリを作成し、かつ不要なシンボルテーブルを ストリップするのがポイントだ。 cdmount.cでは、余分な処理は一切省略してある。 ただ、mount()が成功したかどうかの戻り値を、 main()returnで返すようにしてある。

ここで試しに、

# /tmp/cdmount /dev/cdrom /mnt/cdrom iso9660

と実行してみるとよい。 これで普通にCD-ROMがマウントできればOKだ。


initrd-game.imgの作成

Linuxには、/sbin/mkinitrdという、initrdを作成するための シェルスクリプトが用意されている。これは、SCSI HDDを“/”として マウントするシステムにおいて、カーネルのブート時にSCSIモジュールを ロードさせるためのinitrdを作成するのに必要になるコマンドだ。

initrdはLinuxのext2ファイルシステムイメージであり、 これをgzipで圧縮してブート時に読み込ませる。

ここでは、mkinitrdが作成するinitrdをテンプレートとして、 それに修正を加える方法でinitrd-gameを作成する。

具体的な作業手順を以下に示す。

(initrdのテンプレートの作成)
----
# /sbin/mkinitrd -v -f /boot/initrd-game.img 2.2.14-1vl6
# zcat /boot/initrd-game.img > /tmp/initrd-game  ← gzip展開し、/tmp以下に置く
----
(initrdの中身の修正)
----
# mkdir /mnt/tmp
# mount -o loop /tmp/initrd-game /mnt/tmp  ← ext2としてループバックマウント
# cd /mnt/tmp                              ← initrd-gameの内部に入る
# mkdir -p mnt/cdrom
# mv bin dev etc lib mnt/cdrom
# ln -s mnt/cdrom/* .  ← bin dev etc lib のシンボリックリンク
# ln -s mnt/cdrom/usr mnt/cdrom/sbin mnt/cdrom/var mnt/cdrom/root .
  ↑ これは、この時点ではシンボリックリンク先が存在しないことに注意
# touch fastboot    ← ブート時にfsckさせないようにする
# mkdir proc
# mkdir tmp
# chmod 1777 tmp
# cp -a /dev/hd[abcd]* /dev/scd0 /dev/sda* mnt/cdrom/dev
# cp -p /tmp/cdmount .   ← 先に作成したcdmountのバイナリをここにコピーする
# vi linuxrc
  … linuxrcの修正は以下参照 …
# cd /                         ← initrd-gameから抜ける
# umount /mnt/tmp              ← ループバックマウントを解除
# /sbin/losetup -d /dev/loop0  ← /etc/mtabを変更しているため
                                  “losetup -d”が明示的に必要。
# gzip -9 < /tmp/initrd-game > /boot/initrd-game.img  ← gzip圧縮して保存する
----

linuxrcの中には、mkinitrd によってすでに記述されている行があるが、 これはそのまま残しておく。 その後ろに以下ように記述し、linuxrcを作成する。

●リスト linuxrc の作成
#!/bin/sh   ← ここはすでに記述されている

echo "Loading aic7xxx module"  ← SCSIシステムの場合はこのような記述がある
insmod /lib/aic7xxx.o          ← IDEシステムにはない。

/cdmount /dev/hdc /mnt/cdrom iso9660 ||
/cdmount /dev/hdd /mnt/cdrom iso9660 ||
/cdmount /dev/hdb /mnt/cdrom iso9660 ||
/cdmount /dev/scd0 /mnt/cdrom iso9660 ||
(
  echo 'cannot mount CD-ROM, invoke /bin/sh'
  /bin/sh
)
     
mount -t proc none /proc
echo 0x100 > /proc/sys/kernel/real-root-dev
umount /proc

以上、少々ややこしいが肝心なところなのでよく理解して欲しい。


ここまでの結果、initrd-gameの中身のディレクトリ構成は 以下のようになっているはずである。

initrd-gameの中身
 /sbin -> mnt/cdrom/sbin
 /bin -> mnt/cdrom/bin
 /usr -> mnt/cdrom/usr
 /lib -> mnt/cdrom/lib
 /etc -> mnt/cdrom/etc
 /var -> mnt/cdrom/var
 /dev -> mnt/cdrom/dev
 /root -> mnt/cdrom/root
 /
 /tmp/
 /proc/
 /
 /linuxrc
 /cdmount
 /fastboot
 /
 /mnt/cdrom/bin/sh
           /   /insmod
           /
           /lib/aic7xxx.o  ← SCSIモジュール(IDEシステムの場合はない)
           /
           /etc/
           /dev/console
               /null
               /hdc
               /scd0
               /hda1
                 :

mkinitrdが作成した/bin などのディレクトリは、/mnt/cdrom 以下にシンボリックリンクで飛ばしている。 /mnt/cdrom以下には、CD-ROMをマウントする前にも必要な、 最低限のファイルがある。 たとえば、/bin/shの実体は/mnt/cdrom/bin/shにある。

linuxrcの中で、先に作成したcdmountコマンドが実行され、 CD-ROMがマウントされる。 ここで、CD-ROMのデバイスとして/dev/hdc,hdd,hdb,scd0の順に、 cdmount をシェル文法の“||”でつないで実行していることに注意して欲しい。 つまり、IDEおよびSCSIの、可能性のあるCD-ROMデバイスを エラーコードが返らなくなるまで順番にマウントしているのである。 cdmount.cで、mount() の戻り値をちゃんと返すようにしたのはこのためである。

CD-ROMは、/mnt/cdromディレクトリに覆いかぶさるようにマウントされる。 すると、もと/mnt/cdromの下にあった/bin/dev などはもう見えなくなり、代わりにCD-ROM上の/bin/dev が見えるようになる。

ところで、すべてのデバイスについてCD-ROMのマウントが失敗した場合は、 'cannot mount CD-ROM, invoke /bin/sh'とメッセージを出して シェルを起動するようにしてある。これは、CD-ROMを焼く前に HDDでテストする際に、このシェルを利用するためだ。

CD-ROMのマウント後、linuxrcは今度はCD-ROM内の本物のmount コマンドを用いて、/procファイルシステムをマウントする。 そして、/proc内のreal-root-devに、initrd を示すデバイス番号である、0x100 を書き込んで、正規の“/”としてinitrd-game自身を指定する。

なお、initrd-game内に/fastbootという空のファイルを作って ファイルシステムがfsckされるのを防いでいるのに注意して欲しい。 これを忘れると、あとでCD-ROMのみでブートした際に、/etc/fstab にある古い記述によってHDDをfsckしようとしてしまい、 その際にデバイスが存在しないため、システムの起動が不能になってしまうのだ。

initrd-gameの内部の修正が済んだら、 umountしてからイメージファイルをgzipで圧縮し、 /boot/initrd-game.imgとして保存する。



initrd-game.imgのブートテスト

以上でinitrd-game.imgが作成できたはずだ。 まず、このinitrd-game.imgを、HDD上のLILOを使ってブートのテストを行なう。

ここで、HDD上の/etc/lilo.confは以下のようになっているはずだ。

●リスト HDD上のlilo.conf
boot=/dev/hda
map=/boot/map
install=/boot/boot.b
prompt
timeout=50
append="apm=on"
default=linux

image=/boot/vmlinuz-2.2.14-1vl6
        label=linux
        read-only
        root=/dev/hda1

このうしろに、以下のように追記する。

●リスト HDD上のlilo.confに追加する行
image=/boot/vmlinuz-2.2.14-1vl6
        label=game
        initrd=/boot/initrd-game.img
        root=0x101    ← 実際の値は無視されるが、必ずroot=0x100以外にする

そしてLILOをインストールする。

# /sbin/lilo    ← LILOのインストール
Added linux *
Added game

この状態で、HDDを使って起動テストする。 なお、この時点ではCD-ROMを挿入してはいけない。

マシンをリーブートし、LILO boot: のプロンプトにすぐに gameと入力する。 すると、vmlinuzinitrdのロード後、initrd-game の中のlinuxrcで、 CD-ROMをマウントしようとして失敗し、シェルが起動されるはずだ。 そこで、cdmountコマンドで、HDDを以下のように手動でマウントする。

# /cdmount /dev/hda1 /mnt/cdrom ext2  
           =========
               |
               +--- 実際のHDDのパーティションに応じて変えること

この状態ではHDDはリードオンリーでマウントされており、 これが、あとでCD-ROMにするためのテストになっているのだ。

cdmountコマンドの実行直後、ls など、通常のUNIXコマンドが 使えるようになっていればOKである。

この状態からexitでシェルを抜けると、 linuxrcが終了し、/sbin/init に制御が移って、システムが起動する。 そして以前のテストと同様に、ゲームが起動するはずだ。


To 『Linux CD-ROM ゲームシステム』index


このページは、(株)アスキー TECH Linux Vol. 2 『CD-ROMだけですぐに遊べる! Linux ゲームシステム全解説!!』の原稿を元に、Web 用に再構成したものです。
To 謎の処理系 SunOS 4.1.4 Home
YAMAMORI Takenori 山森丈範
yamamori@kt.rim.or.jp