initramfsについて
TRANSCRIPT
initramfs についてKansai Debian Meeting 20101024
西山和広Good-Day Inc.
Powered by Rabbit 0.6.4
Who am I?twitter: @znz
fingerprint
sec 4096R/B4222F7A 2010-06-27 [expires: 2040-08-10] Key fingerprint = B863 D6DC C2B9 57B8 5238 6CE0 262E D8DB B422 2F7Auid Kazuhiro NISHIYAMA <zn@...>uid Kazuhiro NISHIYAMA (ZnZ) <zn@...>uid Kazuhiro NISHIYAMA <nisiyama@...>uid Kazuhiro NISHIYAMA (znz) <kzhr.nsym@...>ssb 4096R/FAFB96B8 2010-06-27
1/52
Agendainitrd/initramfs とは?
いろいろなところからの Linux の起動
initramfs について
initramfs のカスタマイズ
./init の処理内容
2/52
initrd/initramfs とは?Linux の起動途中に使われる root ファイルシステム
この中で本当の root ファイルシステム (real root) をマウント
実体は gzip された cpio アーカイブ昔は ext2 のディスクイメージファイル
gzip の代わりに lzma のこともある(casper/initrd.lz)
3/52
Linux の起動いろいろなところからの Linux の起動についてinitramfs が起動中のどの部分になるのかの説明のため
4/52
USB/HDD から起動BIOS (起動順位で USB/HDD が上)
MBR (grub などのブートローダー)
vmlinuz (カーネル) + initrd (の中の /init)
real root (/dev/sda1 とか) の /sbin/init(MD, LVM2, LUKS などでも OK)
/etc/inittab の処理とか
5/52
光学ドライブから起動BIOS (起動順位で光学ドライブが上)
El Torito (isolinux などのブートローダー)
vmlinuz (カーネル) + initrd (の中の /init)
real root (filesystem.squashfs + aufs とか) の /sbin/init
/etc/inittab の処理とか
6/52
ネットワークから起動BIOS (起動順位で NIC が上)
PXE boot (pxelinux などのブートローダー)
vmlinuz (カーネル) + initrd (の中の /init)
real root (NFS とか) の /sbin/init
/etc/inittab の処理とか
7/52
real root の場所カーネルに root=UUID=xxx などで指定
ローカルディスク (root=/dev/sda1 など)
光学ドライブ (root=/dev/hdc など)
NFS (nfsroot=192.168.0.1:/path/to/nfsroot など)
など
8/52
/proc/cmdline (1)カーネルのコマンドライン引数
カーネルパラメーター
起動後に /proc/cmdline で見えるもの
grub などで vmlinuz の後ろに書いているもの
9/52
/proc/cmdline (2)initramfs の処理で使うものが多い
カーネル自体が処理するものもある
real root で起動するプログラムが参照する目的で使っても良いが、カーネルや initramfs が使うものと衝突しないように注意
10/52
/proc/cmdline (3)良く使われるものの例
quiet
起動中のコンソールへの出力を減らす
ro / rw
initramfs の中で real root を readonly mount するかどうか
11/52
/proc/cmdline (4)init=/path/to/real_init
/sbin/init の代わりに実行するプログラムを指定
acpi=off apm=off など
カーネルが処理
text
/etc/init.d/gdm3 が「grep -wqs text /proc/cmdline」でチェックしている
12/52
/proc/cmdline (5)root=/path/to/blockdevice
ルートファイルシステムとしてマウントするデバイスを指定
boot=local / boot=nfs / boot=casper / boot=live
real root を mount するのに使うスクリプトを指定
13/52
update-initramfsinitramfs はパッケージの中身ではない
update-initramfs コマンドで生成や更新(カーネルのパッケージのインストール時などは dpkg-trigger で遅延実行)
/usr/share/initramfs-tools/ や /etc/initramfs-tools/ を元に生成
「sudo update-initramfs -u -k all」などで更新
14/52
initramfs の調べ方
mkdir -p /tmp/initrd && (cd /tmp/initrd && { zcat /boot/initrd.img-* | cpio -idm; })
# initramfs-tools(8)mkdir tmp/initramfscd tmp/initramfsgunzip -c /boot/initrd.img-2.6.18-1-686 | \cpio -i -d -H newc --no-absolute-filenames
# linux-2.6/Documentation/initrd.txtmkdir /tmp/imagefilecd /tmp/imagefilegzip -cd /boot/imagefile.img | cpio -imd --quiet
などのように展開(カレントディレクトリにばらまかれるので展開場所には注意)
15/52
initramfs の中身 (1)./init
カーネルが実行するプログラム
Debian 系の場合は /bin/sh のシェルスクリプト✓
Redhat 系の場合は以前確認したときは nash だった
✓
./scripts
./init から読み込まれたり実行されたりするプログラム
16/52
initramfs の中身 (2)./bin や ./sbin
busybox など
./conf や ./etc
設定ファイル
./lib や ./usr
ライブラリやカーネルモジュールなど
17/52
initramfs のカスタマイズ/etc/initramfs-tools/ 以下のファイルを編集したり、ファイルを追加したり。
Debian Live のようにパッケージなら /usr/share/initramfs-tools/ 以下にファイルを追加する。
18/52
/etc/initramfs-tools (1)initramfs.conf
update-initramfs.conf
update-initramfs や mkinitramfs の設定ファイル
modules
initramfs の中でロードするモジュール(後でロードすればいいものは /etc/modules を使う)
19/52
/etc/initramfs-tools (2)conf.d/
initramfs の conf/conf.d/ に入る。
hooks/
/usr/share/initramfs-tools/hooks/ と同様に initramfs 作成時に実行される。
20/52
/etc/initramfs-tools (3)scripts/
/usr/share/initramfs-tools/scripts/ と一緒に initramfs の scripts/ に入る。
(リカバリディスクを作成するときに scripts/local-premount/recovery を作った。)
21/52
hooks//usr/share/initramfs-tools/hook-functions の関数で initramfs の中身を生成
copy_exec で追加しておきたいバイナリをコピー(ldd でわかる範囲内で使っているライブラリを含めてコピーされる)
manual_add_modules でモジュールをコピー
その他のファイルを追加や削除など
22/52
scripts/カスタマイズする処理本体
live 関係なら以下のようなものが入る。(BOOT=live のときに使われる)
live
live-bottom/
live-functions
live-helpers
live-premount/
23/52
./init の処理内容 (1)ソースは initramfs-tools 0.98.4 から。(initramfs-tools 由来のコードのライセンスは GPL2 or later です)
#!/bin/sh
echo "Loading, please wait..."
このメッセージが出たところからが initramfs の中の処理
24/52
./init の処理 (2) dirinitramfs 内の ディレクトリの準備
[ -d /dev ] || mkdir -m 0755 /dev[ -d /root ] || mkdir -m 0700 /root[ -d /sys ] || mkdir /sys[ -d /proc ] || mkdir /proc[ -d /tmp ] || mkdir /tmpmkdir -p /var/lockmount -t sysfs -o nodev,noexec,nosuid none /sysmount -t proc -o nodev,noexec,nosuid none /proc
25/52
./init の処理 (3) devinitramfs 内の /dev や udev の準備
# Note that this only becomes /dev on the real filesystem if udev's scripts# are used; which they will be, but it's worth pointing outtmpfs_size="10M"if [ -e /etc/udev/udev.conf ]; then . /etc/udev/udev.conffiif ! mount -t devtmpfs -o mode=0755 none /dev; then echo "W: devtmpfs not available, falling back to tmpfs for /dev" mount -t tmpfs -o size=$tmpfs_size,mode=0755 udev /dev [ -e /dev/console ] || mknod -m 0600 /dev/console c 5 1 [ -e /dev/null ] || mknod /dev/null c 1 3fimkdir /dev/ptsmount -t devpts -o noexec,nosuid,gid=5,mode=0620 none /dev/pts || true> /dev/.initramfs-toolsmkdir /dev/.initramfs
26/52
./init (4) export後で起動するシェルスクリプトなどのために、いくつかのシェル変数は環境変数に後半はスペースの都合で同じ行に詰めています
# Export the dpkg architectureexport DPKG_ARCH=. /conf/arch.conf
# Set modprobe envexport MODPROBE_OPTIONS="-qb"
# Export relevant variablesexport ROOT=; export ROOTDELAY=; export ROOTFLAGS=; export ROOTFSTYPE=export IP=; export BOOT=; export BOOTIF=; export UBIMTD=; export break=export init=/sbin/init; export quiet=n; export readonly=yexport rootmnt=/root; export debug=; export panic=; export blacklist=export resume=; export resume_offset=
27/52
./init (5) confconf ファイルの読み込み
# Bring in the main config. /conf/initramfs.conffor conf in conf/conf.d/*; do [ -f ${conf} ] && . ${conf}done. /scripts/functions
28/52
./conf/initramfs.conf./conf/initramfs.conf の中身の例(real root を探す処理が BOOT で決まる)
% egrep '^[^#]' conf/initramfs.confMODULES=mostBUSYBOX=yKEYMAP=nCOMPRESS=gzipBOOT=localDEVICE=NFSROOT=auto
29/52
./conf/conf.d の中身の例
% ls conf/conf.dresume% cat conf/conf.d/resumeRESUME=UUID=ae891314-2104-480c-bdd8-6de40e6edb72
30/52
./init (6) cmdlineカーネルのコマンドライン引数 (/proc/cmdline) の解析
# Parse command line optionsfor x in $(cat /proc/cmdline); do case $x in init=*) init=${x#init=} ;; root=*) ROOT=${x#root=} case $ROOT in LABEL=*) ROOT="${ROOT#LABEL=}"(省略)
31/52
./init (7) noresume
if [ -n "${noresume}" ]; then export noresume unset resumeelse resume=${RESUME:-}fi
# cmdline 処理の一部: resume=*) RESUME="${x#resume=}" ;; resume_offset=*) resume_offset="${x#resume_offset=}" ;; noresume) noresume=y ;;
32/52
./init (8) netconsole
[ -n "${netconsole}" ] && \modprobe netconsole netconsole="${netconsole}"
# cmdline 処理の一部: netconsole=*) netconsole=${x#netconsole=} ;;
スペースの都合で改行を入れています
33/52
./init (9) maybe_breakmaybe_break は処理の途中でシェルを起動できる場所
maybe_break top
# cmdline 処理の一部: break=*) break=${x#break=} ;; break) break=premount ;;
34/52
scripts/functions: maybe_break (1)
# scripts/functions の一部:maybe_break(){ if [ "${break:-}" = "$1" ]; then panic "Spawning shell within the initramfs" fi}
35/52
scripts/functions: maybe_break (2)
ubuntu (lucid) の initramfs-tools 0.92bubuntu78(以降 ubuntu 版の話がある場合はこのバージョ
ン)の場合、Debian と違って複数指定可能
# scripts/functions の一部:maybe_break(){ if echo "${break}" | egrep -q "(,|^)$1(,|$)"; then panic "Spawning shell within the initramfs" fi}
36/52
functions: panic (1)起動時に root ファイルシステムが見つからなくて「(initramfs)」で止まることがあるのはこれ。
# scripts/functions の一部:panic(){ if [ -x /sbin/usplash_write ]; then /sbin/usplash_write "QUIT" fi
if command -v chvt >/dev/null 2>&1; then chvt 1 fi
# Disallow console access if [ -n "${panic}" ]; then sleep ${panic} reboot fi modprobe i8042 modprobe atkbd echo "$@" REASON="$@" PS1='(initramfs) ' /bin/sh -i </dev/console >/dev/console 2>&1}
37/52
functions: panic (2)数字か「.」以外の文字があると無視するようになっている。
# cmdline 処理の一部: panic=*) panic="${x#panic=}" case ${panic} in *[![:digit:].]*) panic= ;; esac ;;
38/52
functions: panic (3)ubuntu 版だと run_scripts がある。
# scripts/functions の一部:panic(){ if [ -x /sbin/usplash_write ]; then /sbin/usplash_write "QUIT" fi chvt 1
# Disallow console access if [ -n "${panic}" ]; then sleep ${panic} reboot fi
modprobe i8042 modprobe atkbd
run_scripts /scripts/panic
echo $@ PS1='(initramfs) ' /bin/sh -i </dev/console >/dev/console 2>&1}
39/52
run_scripts (1)指定されたディレクトリの中の実行可能ファイルを実行
prereqs で依存関係を指定できる
ORDER に tsort (トポロジカルソート) 結果をキャッシュしている
40/52
run_scripts (2)
# scripts/functions の一部:run_scripts(){ initdir=${1} [ ! -d ${initdir} ] && return
if [ -f ${initdir}/ORDER ]; then . ${initdir}/ORDER elif command -v tsort >/dev/null 2>&1; then runlist=$(get_prereq_pairs | tsort) call_scripts ${2:-} else get_prereqs reduce_prereqs call_scripts fi}
41/52
prereqs引数が prereqs の時に依存するものを echo する。(複数ある場合はスペース区切りで列挙)
PREREQ=""
prereqs(){
echo "$PREREQ"}
case $1 in# get pre-requisitesprereqs)
prereqsexit 0;;
esac
42/52
./init (10) init-topフックを実行
# Don't do log messages here to avoid confusing usplashrun_scripts /scripts/init-top
43/52
init (11) load_modules./conf/modules に書いてあるモジュールをロード(/etc/initramfs-tools/modules で設定)
maybe_break modules[ "$quiet" != "y" ] && log_begin_msg "Loading essential drivers"load_modules[ "$quiet" != "y" ] && log_end_msg
自動認識できないモジュールやはやめにロードしないといけないモジュールに使う。
44/52
./init (12) mountmountroot は「. /scripts/${BOOT}」で定義される。(parse_numeric は ROOT=major:minor のような指定を処理している)
maybe_break premount[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/init-premount"run_scripts /scripts/init-premount[ "$quiet" != "y" ] && log_end_msg
maybe_break mountlog_begin_msg "Mounting root file system". /scripts/${BOOT}parse_numeric ${ROOT}maybe_break mountrootmountrootlog_end_msg
45/52
./script/local
run_scripts /scripts/local-top
のようなフックを実行したり"${ROOT}" で指定されているデバイスをマウントしたりマウントできなかったときに
panic "ALERT! ${ROOT} does not exist. Dropping to a shell!"
で「(initramfs) 」のシェルに落ちたりする。
46/52
./init (13) bottom最初の方でマウントした sysfs と proc を本当の root ファイルシステム (real root) に移動する。
maybe_break bottom[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/init-bottom"run_scripts /scripts/init-bottom[ "$quiet" != "y" ] && log_end_msg
# Move virtual filesystems over to the real filesystemmount -n -o move /sys ${rootmnt}/sysmount -n -o move /proc ${rootmnt}/proc
47/52
./init (14) check init
validate_init() { checktarget="${1}"
# Work around absolute symlinks if [ -d "${rootmnt}" ] && [ -h "${rootmnt}${checktarget}" ]; then case $(readlink "${rootmnt}${checktarget}") in /*) checktarget="$(chroot ${rootmnt} readlink ${checktarget} )" ;; esac fi
# Make sure the specified init can be executed if [ ! -x "${rootmnt}${checktarget}" ]; then return 1 fi
# Upstart uses /etc/init as configuration directory :-/ if [ -d "${rootmnt}${checktarget}" ]; then return 1 fi}
48/52
./init (15) check init
# Check init bootargif [ -n "${init}" ]; then if ! validate_init "$init"; then echo "Target filesystem doesn't have requested ${init}." init= fifi
# Common case: /sbin/init is presentif [ ! -x "${rootmnt}/sbin/init" ]; then # ... if it's not available search for valid init if [ -z "${init}" ] ; then for inittest in /sbin/init /etc/init /bin/init /bin/sh; do if validate_init "${inittest}"; then init="$inittest" break fi done fi
# No init on rootmount if ! validate_init "${init}" ; then panic "No init found. Try passing init= bootarg." fifi
49/52
./init (16) unsetenv
# don't leak too much of env - some init(8) don't clear it# (keep init, rootmnt)unset debugunset MODPROBE_OPTIONSunset DPKG_ARCHunset ROOTFLAGSunset ROOTFSTYPEunset ROOTDELAYunset ROOTunset IPunset BOOTunset BOOTIFunset UBIMTDunset blacklistunset breakunset noresumeunset panicunset quietunset readonlyunset resumeunset resume_offset
50/52
./init (17) exec initklibc-utils パッケージ由来の run-init で real root の /sbin/init などを実行
# Chain to real filesystemexec run-init ${rootmnt} ${init} "$@" \ <${rootmnt}/dev/console \ >${rootmnt}/dev/consolepanic "Could not execute run-init."
51/52
real root での処理sysvinit
/etc/inittab に従って処理
upstart
/etc/init/*.conf に従って処理
など
52/52Powered by Rabbit 0.6.4