Initrd這個(gè)設(shè)計(jì)的初衷是用來加載額外內(nèi)核模塊供啟動(dòng)的??梢詤⒖?內(nèi)核文檔 Documentation/initrd.txt。
在加載完內(nèi)核后,如果存在initrd,則會(huì)執(zhí)行 initrd 里的 /init。(文檔里說的是/linuxrc,在 init/do_mounts_initrd.c 里也是這個(gè),在 init/main.c 里是 /init,具體待考。)
進(jìn)入主題:簡單說來,initrd 主要功能就一個(gè):找到根分區(qū),把權(quán)力交給主系統(tǒng)。
要完成這個(gè)功能,涉及的功能主要有:
1.有基本的程序運(yùn)行環(huán)境
2.檢測存儲設(shè)備,創(chuàng)建設(shè)備節(jié)點(diǎn)
3.檢測文件系統(tǒng),掛載根文件系統(tǒng)
4.將權(quán)力交給主系統(tǒng)的init
一.基本的程序運(yùn)行環(huán)境
initrd 主要有兩種格式:
1. 傳統(tǒng)的 ramdisk
這種格式的好處是還可以返回到 initrd,進(jìn)行些后繼的處理。
缺點(diǎn)是需要內(nèi)核的文件系統(tǒng)支持,通常會(huì)用 ext2,且更改較為麻煩。
制作方法:
dd if=/dev/zero of=initrd bs=1M count=8
mkfs.ext2 -f -m 0 initrd
mount -o loop initrd /path/to/
在/path/to建立好initrd的系統(tǒng)后
umount /path/to
gzip initrd
2. cpio 格式
這種格式的好處是內(nèi)核原生不需要額外的文件系統(tǒng)支持,制作也比較容易。
制作方法:
cd /path/to
find . |cpio -o -H newc |gzip -c > ../initrd.gz
如果沒有特別的需要,通常使用cpio格式。
找到根文件系統(tǒng)的任務(wù)通常是用shell腳本來完成,主要原因是:
1.體積所限,通常initrd不會(huì)做很大,因?yàn)樗δ芎苊鞔_單一
2.方便修改,針對不同硬件/系統(tǒng)通常會(huì)做一定更改,編譯型語言更改起來較麻煩
通常使用的shell有busybox的ash,klibc的sh等。
busybox提供很多功能,可根據(jù)自己的需要編譯,因?yàn)橐С謚dev等,所以推薦編譯成動(dòng)態(tài)鏈接的。
klibc是專門設(shè)計(jì)為小巧的libc,它自帶了一些程序,體積很小巧,功能相對busybox提供的不會(huì)那么多。
還有相關(guān)的程序。對于很單一的應(yīng)用用它是合適的,如果想在initrd里實(shí)現(xiàn)較復(fù)雜功能,使用klibc會(huì)顯得有些捉襟見肘。
對于動(dòng)態(tài)鏈接的程序,需要把相應(yīng)的庫和 helper 放進(jìn)系統(tǒng)中??捎胠dd實(shí)現(xiàn)這個(gè)功能,如:
$ ldd /bin/busybox
linux-gate.so.1 => (0xffffe000)
libcrypt.so.1 => /lib/libcrypt.so.1 (0xb7f30000)
libm.so.6 => /lib/libm.so.6 (0xb7f0b000)
libc.so.6 => /lib/libc.so.6 (0xb7de1000)
/lib/ld-linux.so.2 (0xb7f73000)
我們也提供了一個(gè)程序來自動(dòng)完成這個(gè)工作,見文后參考。
對于klibc的,一般會(huì)有個(gè)庫文件,例如 /usr/lib/klibc/lib/klibc-KC4v-FjcUUw8mDjRL-kY8PS8U3E.so
將此文件放在相對initrd的根目錄的 /lib 目錄下即可。
需要的設(shè)備有:
mknod dev/console c 5 1
mknod dev/null c 1 3
# 如果為ramdisk,最好創(chuàng)建它
mknod dev/ram0 c 1 0
二.檢測存儲設(shè)備
早先有 devfs hotplug等來檢測,或是靜態(tài)創(chuàng)建加載模塊以支持存儲設(shè)備如硬盤,光盤等。
udev的出現(xiàn)使這個(gè)過程轉(zhuǎn)移到用戶空間,靈活性大大增強(qiáng),使這個(gè)過程能自動(dòng)完成。
對于新版的 udev (大概是>098),完成這一過程只需寫好相應(yīng)的規(guī)則,有相應(yīng)的模塊,執(zhí)行以下命令即可自動(dòng)加載模塊并創(chuàng)建相應(yīng)的設(shè)備節(jié)點(diǎn):
# 掛載所需文件系統(tǒng)
mount -t proc none /proc
mount -t sysfs none /sys
mount -t tmpfs -o size=10M none /dev
# 關(guān)閉內(nèi)核消息打印
echo '0' > /proc/sys/kernel/printk
# 一些連接
ln -sf /proc/self/fd /dev/fd
ln -sf /proc/self/fd/0 /dev/stdin
ln -sf /proc/self/fd/1 /dev/stdout
ln -sf /proc/self/fd/2 /dev/stderr
ln -sf /proc/kcore /dev/core
# 啟動(dòng) udev
/sbin/udevd --daemon
/sbin/udevtrigger
/sbin/udevsettle
三.檢測文件系統(tǒng)
由于各系統(tǒng)和內(nèi)核的不同,掛載文件系統(tǒng)有時(shí)不像我們平時(shí)使用的那樣,會(huì)自動(dòng)探測,可以使用udev或blkid來識別:
udev的方法:
# /lib/udev/vol_id -t /dev/hda5
xfs
blkid的方法:
# blkid
/dev/hda1: TYPE="ntfs"
/dev/hda5: LABEL="Gentoo" UUID="78460951-666e-4d29-9d9b-85e9a9b16b62" TYPE="xfs"
/dev/hda6: TYPE="ntfs"
/dev/hda8: UUID="c51d3bb4-caee-4150-ae22-7d5931db31f5" LABEL="ROOT" TYPE="reiserfs"
/dev/hda9: LABEL="Home" UUID="89b31278-b2a3-4626-99c5-e6ca77fe60f0" TYPE="xfs"
/dev/hda7: LABEL="swap" UUID="8686fb94-560c-4c87-97dd-c5f97bbb6c78" TYPE="swap"
另外,我們也可以使用UUID或者LABEL的方式來掛載文件系統(tǒng),
mount -U
mount -L
四.交權(quán)給主系統(tǒng)的init
實(shí)現(xiàn)方法主要有 pivot_root 和 switch_root與 run-init。
在這之前需要對之前掛載的虛擬文件系統(tǒng)與dev轉(zhuǎn)移到主系統(tǒng)。
mount --move /dev /root/dev
mount --move /proc /root/proc
mount --move /sys /root/sys
1.pivot_root
傳統(tǒng)的做法是用 pivot_root,然而 pivot_root不會(huì)自動(dòng)處理很多工作,如釋放initrd所有的內(nèi)存,執(zhí)行主系統(tǒng)的init。
使用方法如下:
cd /root
mkdir -p initrd
pivot_root . initrd
# 注意 /root 要是合法的設(shè)備掛載上的 如 /dev/sda1,如果不是這樣,將可能出現(xiàn)錯(cuò)誤。
# 目標(biāo)目錄也需要存在,其它參閱man page
exec chroot . sh -c 'umount /initrd; exec /sbin/init' dev/console 2>&1
# 如果不需要清理,也可簡單的運(yùn)行
exec chroot . /sbin/init
2.switch_root/run-init
這兩者差別不大,前者是busybox提供的,后者是klibc的。有點(diǎn)區(qū)別的是后者可接受 - 開頭的參數(shù)。
用法:
cd /root
exec switch_root . /sbin/init
或
exec run-init . /sbin/init --debug
至此,initrd的使命完成。