続・玩式草子 ―戯れせんとや生まれけん―

第11回 Plamo-7.1とinitrd[2]

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

前回Plamo-7.1から採用することにしたinitrdの概要を紹介しました。その際に触れたように,initrdはドライバ・モジュールを組み込むことに特化したLinuxシステムで,機能はシンプルなものの,Linuxシステムを運用するためのファイル一式が必要なことを紹介しました。

それらのファイルを過不足なく揃え,適切に配置してinitrdイメージを作成するのがmkinitramfsスクリプトです。今回はこのスクリプトの処理を解説しながらinitrdの作り方を紹介しましょう。

/sbin/mkinitramfs

前回,initrd上のinitスクリプトをつまみ読みしながら処理の流れを概観したように,initrd上には「ルートファイルシステムをマウントするために必要なドライバ・モジュール「それらモジュールをカーネルに組み込むためのコマンド(実行ファイル)」⁠⁠それら実行ファイルが必要とする共有ライブラリなどが必要となります。mkinitramfsはこれら必要なファイルを集めてinitrdイメージを作るツールです。

mkinitramfsはシンプルなシェルスクリプトなものの,全文を掲載するには長すぎるので,つまみ読みしながら処理の流れを紹介してみます。このスクリプトは,見だしのようにPlamo-7.1環境では/sbin/mkinitramfsにありますので,全体を見たい方はそちらでご確認ください。また,このコードのオリジナルはBLFSのサイトで参照できます。

必要なコマンドの準備

mkinitramfsでは,最初の30行ほどで内部処理用のcopyコマンドを定義したり,ドライバ・モジュールのディレクトリを確認するなどの初期化処理を行った後,initrd上で使うコマンドを$binfiles$sbinfilesに指定していきます。

 36  printf "Creating $INITRAMFS_FILE... "
 37  
 38  binfiles="sh cat cp dd killall ls mkdir mknod mount "
 39  binfiles="$binfiles umount sed sleep ln rm uname"
 40  binfiles="$binfiles readlink basename"
 ...
 45  sbinfiles="modprobe blkid switch_root"
 46  
 47  #Optional files and locations
 48  for f in mdadm mdmon udevd udevadm; do
 49    if [ -x /sbin/$f ] ; then sbinfiles="$sbinfiles $f"; fi
 50  done

これらのうち,$binfilesに指定したコマンド(の実行ファイル)はinitrdのbinディレクトリに,$sbinfilesに指定した実行ファイルはsbinディレクトリに,それぞれコピーされます。

また,もう少し先でkmodlvm, dmsetupが追加されるので,initrdに含まれる実行ファイルは総計27個となります。

作業用ディレクトリの作成

次に/tmpディレクトリに作業用の一時ファイル($unsorted)とディレクトリ($WDIR)を用意します。

 52  unsorted=$(mktemp /tmp/unsorted.XXXXXXXXXX)
 ...
 57  # Create a temporary working directory
 58  WDIR=$(mktemp -d /tmp/initrd-work.XXXXXXXXXX)
 59  

$unsortedは先に指定した27個の実行ファイルが必要とする共有ライブラリを調べる際に使うファイルで,後述するように各実行ファイルにlddした結果を収めていきます。一方,$WDIRはCPIOイメージを作る際の作業ディレクトリになります。

次に,$WDIRにディレクトリ構造を作ります。initrdはシンプルなものの独立したLinux環境なので,ルートファイルシステムのディレクトリ構造一式が必要となります。

 60  # Create base directory structure
 61  mkdir -p $WDIR/{bin,dev,lib/firmware,run,sbin,sys,proc,usr}
 62  mkdir -p $WDIR/etc/{modprobe.d,udev/rules.d}
 63  touch $WDIR/etc/modprobe.d/modprobe.conf
 64  ln -s lib $WDIR/lib64
 65  ln -s ../bin $WDIR/usr/bin

initrd用の/dev/console/dev/nullを作ります。ほとんどのデバイスファイルはudevdが動的に作るものの,udevdのメッセージを表示したり,不要なメッセージを捨てたりするデバイスファイルはあらかじめ用意しておく必要があります。

 67  # Create necessary device nodes
 68  mknod -m 640 $WDIR/dev/console c 5 1
 69  mknod -m 664 $WDIR/dev/null    c 1 3

実行ファイルと共有ライブラリのコピー

まず,udev用の設定ファイルやルールファイルを,動作中の環境から作業用ディレクトリへコピーします。

 71  # Install the udev configuration files
 72  if [ -f /etc/udev/udev.conf ]; then
 73    cp /etc/udev/udev.conf $WDIR/etc/udev/udev.conf
 74  fi
 75  
 76  for file in $(find /etc/udev/rules.d/ -type f) ; do
 77    cp $file $WDIR/etc/udev/rules.d
 78  done

代入している部分は省略したものの,89行目で使っている$DATADIR/$INITINは/usr/share/mkinitramfs/init.inで,あらかじめ用意してあるこのファイルを,initrd上のinitとしてインストールします。このファイルは前回紹介しました。

 88  # Install the init file
 89  install -m0755 $DATADIR/$INITIN $WDIR/init

ドライバ・モジュールを組み込むためのkmodを$binfilesのリストに追加します。念のため,古いmodule-init-toolsを使っている場合も考慮しています。

 91  if [  -n "$KERNEL_VERSION" ] ; then
 92    if [ -x /bin/kmod ] ; then
 93      binfiles="$binfiles kmod"
 94    else
 95      binfiles="$binfiles lsmod"
 96      sbinfiles="$sbinfiles insmod"
 97    fi
 98  fi

次に$binfilesに指定したコマンド(実行ファイル)を作業用ディレクトリ内へコピーします。$binfilesにはコマンド名しか指定していないため,実行ファイルが/bin/にあるのか/usr/bin/にあるのかをチェックして(102行目)⁠利用する共有ライブラリの情報を$unsortedに記録(103行目)してから,作業用ディレクトリのbin/以下にコピーしていきます。なお,ここで使っているcopyは,省略した部分(5~22行目)で定義している内部コマンドで,宛先を$WDIR内に限定したコピーです。

100  # Install basic binaries
101  for f in $binfiles ; do
102    if [ -e /bin/$f ]; then d="/bin"; else d="/usr/bin"; fi
103    ldd $d/$f | sed "s/\t//" | cut -d " " -f1 >> $unsorted
104    copy $d/$f bin
105  done

同様に$sbinfilesに指定したコマンド(実行ファイル)を作業用ディレクトリ内へコピーします。こちらの実行ファイルは/sbin/以下にあるはずなので一手間省けますが,共有ライブラリは忘れずにチェックします。ここまでで,initrdに組み込む実行ファイルが必要とする共有ライブラリの一覧(重複あり)が$unsortedに記録されました。

110  for f in $sbinfiles ; do
111    ldd /sbin/$f | sed "s/\t//" | cut -d " " -f1 >> $unsorted
112    copy $f sbin
113  done

次に,$unsortedに集めた共有ライブラリのリストからsortとuniqを使って重複を除き,残ったライブラリを作業ディレクトリのlib/以下にコピーします。155~157行目は,lddコマンドでは共有ライブラリとして表示されるものの,カーネルが提供する仮想的なライブラリで実体は存在しない"linux-vdso.1"や"linux-gate.so.1"を省く処理です。

153  # Install libraries
154  sort $unsorted | uniq | while read library ; do
155    if [ "$library" == "linux-vdso.so.1" ] ||
156       [ "$library" == "linux-gate.so.1" ]; then
157      continue
158    fi
159  
160    copy $library lib
161  done

このあたり,コードとしてはそれほど凝ったことをしているわけではないものの,initrdを自作しようと苦労したことがある人間にとって,これだけの処理でinitrdに必要な共有ライブラリが揃うというのは,結構「目から鱗」な体験でした。

udevは/lib/udevに支援用の実行ファイルがあるので,それらをまとめて作業用ディレクトリにコピーします。

163  if [ -d /lib/udev ]; then
164    cp -a /lib/udev $WDIR/lib
165  fi

著者プロフィール

こじまみつひろ

Plamo Linuxとりまとめ役。もともとは人類学的にハッカー文化を研究しようとしていたものの,いつの間にかミイラ取りがミイラになってOSSの世界にどっぷりと漬かってしまいました。最近は田舎に隠棲して半農半自営な生活をしながらソフトウェアと戯れています。

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