Ubuntu Weekly Recipe

第384回 Initramfsのしくみ

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

update-initramfs

update-iniramfsは,/bootにある既存のInitramfsの更新や削除,/bootへの新規Initramfsの作成に使うコマンドです。カーネルアップデート時のフックスクリプトから呼ばれる前提の作りになっているため,ユーザーが直接呼び出すことはあまりないでしょう。

具体的に呼び出しているのは,/etc/kernel/postinst.d/initramfs-toolsです。/etc/kernel以下にはカーネルのインストールや更新,削除のタイミングで呼び出すべきスクリプトを各パッケージがインストールする場所でもあります。postinst.dはカーネルパッケージのインストール後注3に呼び出すスクリプトを保存します。

注3)
カーネルパッケージの場合は,新しいバージョンであっても常に「新規パッケージのインストール」になります。これは更新にともなって古いカーネルを上書きしてしまうと,更新によって起動できなくなった時に,古いカーネルに戻すことができなくなるためです。
$ ls /etc/kernel/postinst.d/
apt-auto-removal  dkms  initramfs-tools  pm-utils
unattended-upgrades  update-notifier  zz-update-grub

これらのスクリプトは第一引数にカーネルのバージョン,第二引数にカーネルイメージの絶対パスが渡ります。

apt-auto-removalはカーネル更新時に古いカーネルパッケージを自動削除するためのスクリプトで,dkmsはモジュールの再ビルドを行うためのスクリプトです。initramfs-toolsではInitramfsを再構築し,pm-utilsでは再起動するまでハイバネートを禁止します。

unattended-upgradesは/var/run/reboot-requiredファイルを作るだけです。これによりByobuなどの各種ツールやログイン時などに再起動が必要であることが検知できるようになります。update-notifierも役割としては同じではありますが,ファイルの中にgettextによってローカライズされた再起動要求メッセージを保存し,要因となったパッケージを/var/run/reboot-required.pkgsに記録します。zz-update-grubはGRUBの設定ファイルの更新スクリプトです。

このうち/etc/kernel/initramfs-toolsは,第二引数からカーネルイメージがある起動用のパス(/boot)を抽出したうえで,以下のコマンドを実行しています。

$ INITRAMFS_TOOLS_KERNEL_HOOK=1 update-initramfs -c -t -k 3.19.0-23-generic -b /boot

「-c」オプションはInitramfsの新規作成,⁠-t」は既存のファイルの上書きの許可,⁠-k」は作成するカーネルのバージョン,⁠-b」は作成先の起動ディレクトリを意味します。結果的にInitramfsの作成そのものはmkinitramfsコマンドが行っています。

$ mkinitramfs -o /boot/initrd.img-3.19.0-23-generic.new 3.19.0-23-generic

mkinitramfs

mkinitramfsコマンドは/tmp以下にInitramfsのためにルートファイルシステムを構築したうえで,最終的にcpio形式でアーカイブするコマンドです。

/etc/initramfs-tools/modulesや/usr/share/initramfs-tools/hook-functionsの関数の中で設定されているモジュール,/usr/share/initramfs-tools以下のinitやscripts/,/usr/lib/initramfs-tools/bin/や/usr/lib/klibc/bin/以下,さらには/lib/や/bin/以下のいくつかのバイナリをコピーしたうえで,指定した形式で圧縮します。

コピーするものは各種設定ファイルの中でハードコーディングされています。何か追加や削除したい場合は/etc/initramfs-toolsの中のフックスクリプトで変更してください。

ちなみにBusyBoxは,Initramfs用に機能を落としたbusybox-initramfs版のBusyBoxが使われます。使える機能はかなり限られていますので,Initramfs内部にシェルスクリプトを追加する場合は注意してください。

$ /usr/lib/initramfs-tools/bin/busybox
BusyBox v1.22.1 (Ubuntu 1:1.22.0-9ubuntu1) multi-call binary.
(中略)

Currently defined functions:
        [, [[, acpid, ash, awk, basename, blockdev, cat, chmod, chroot, chvt,
        clear, cmp, cp, cut, deallocvt, devmem, df, dnsdomainname, du,
        dumpkmap, echo, egrep, env, expr, false, fbset, fdflush, fgrep, find,
        fstrim, grep, gunzip, gzip, hostname, hwclock, ifconfig, ip, kill, ln,
        loadfont, loadkmap, ls, lzop, lzopcat, mkdir, mkfifo, mknod, mkswap,
        mktemp, modinfo, more, mount, mv, openvt, pidof, printf, ps, pwd,
        readlink, reset, rm, rmdir, sed, seq, setkeycodes, sh, sleep, sort,
        stat, static-sh, stty, switch_root, sync, tail, tee, test, touch, tr,
        true, tty, umount, uname, uniq, unlzop, wc, wget, which, yes, zcat

/usr/lib/initramfs-tools/binには他にも,メモリ上に展開できる圧縮スワップであるramzswapデバイスを操作するためのrzscontrolや,udevを用いて指定したデバイスがマウントされるまで停止するwait-for-rootコマンドも存在します。

Initramfsの起動の流れ

最後に,Initramfsが展開されてからルートファイルシステムをマウントするまでの流れを概観しておきましょう。

カーネルはブートローダーから渡されたInitramfsのアドレスを元に,メモリ上にその内容を展開したあと,⁠rdinit=」「init=」オプションで指定されたスクリプト,もしくは/sbin/init,/etc/init,/bin/init,/bin/shのうち,最初に見つかったスクリプトを起動スクリプトとして実行します。

rdinitの初期値は/initですので,何も指定しなければ実行スクリプトとして使われるのはInitramfsの「/init」です。initスクリプトはprocやsysfsのマウント,/devや/tmpなどの作成を行ったあと,/proc/cmdlineをパースしたうえで必要なプログラムを実行します。

「boot=」オプションに何も指定していない場合,⁠local」スクリプトが取り込まれます。これはいくつかのチェックを経て「root=」で指定されたデバイスを/rootにマウントする,mountroot関数などが定義されているスクリプトです。

マウント済みの/procや/sysをmountコマンドのmoveオプションで/root/以下に移動したら,最後にklibc-utilsパッケージのrun-initコマンドを実行します。run-initでは主に/root以外のInitramfs関連のファイルを削除し,/root(つまりは実際のルートファイルシステム)「/」にマウントしなおしたうえでchroot(2)を実行し,指定した起動スクリプト(/sbin/init)を呼び出します。

実際のルートファイルシステムにchroot(2)で移動したあとですので,この「/sbin/init」は普通のInitプログラムとなります。つまりUbuntu 14.10以前であればUpstart,15.04以降であればsystemdです。

あとはこのInitプログラムに従って起動プロセスが進んでいき,たとえばUbuntuの起動ロゴを表示するplymouthなどもこのタイミングで起動します。よってこのロゴが表示される前に処理が止まるようであれば図1のようにInitramfs内部で何かエラーが発生している可能性が,ロゴの表示以降に処理が止まるのであればInitramfsよりも後ろのどこかで問題が発生した可能性が高いということがわかるのです。

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。