Initrd啟動及功能分析
Initrd這個設計的初衷是用來加載額外內核模塊供啟動的。可以參考 內核文檔 Documentation/initrd.txt。
在加載完內核后,如果存在initrd,則會執行 initrd 里的 /init。(文檔里說的是/linuxrc,在 init/do_mounts_initrd.c 里也是這個,在 init/main.c 里是 /init,具體待考。)
進入主題:簡單說來,initrd 主要功能就一個:找到根分區,把權力交給主系統。
要完成這個功能,涉及的功能主要有:
1.有基本的程序運行環境
2.檢測存儲設備,創建設備節點
3.檢測文件系統,掛載根文件系統
4.將權力交給主系統的init
一.基本的程序運行環境
initrd 主要有兩種格式:
1. 傳統的 ramdisk
這種格式的好處是還可以返回到 initrd,進行些后繼的處理。
缺點是需要內核的文件系統支持,通常會用 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的系統后
umount /path/to
gzip initrd
2. cpio 格式
這種格式的好處是內核原生不需要額外的文件系統支持,制作也比較容易。
制作方法:
cd /path/to
find . |cpio -o -H newc |gzip -c > ../initrd.gz
如果沒有特別的需要,通常使用cpio格式。
找到根文件系統的任務通常是用shell腳本來完成,主要原因是:
1.體積所限,通常initrd不會做很大,因為它功能很明確單一
2.方便修改,針對不同硬件/系統通常會做一定更改,編譯型語言更改起來較麻煩
通常使用的shell有busybox的ash,klibc的sh等。
busybox提供很多功能,可根據自己的需要編譯,因為要支持udev等,所以推薦編譯成動態鏈接的。
klibc是專門設計為小巧的libc,它自帶了一些程序,體積很小巧,功能相對busybox提供的不會那么多。
還有相關的程序。對于很單一的應用用它是合適的,如果想在initrd里實現較復雜功能,使用klibc會顯得有些捉襟見肘。
對于動態鏈接的程序,需要把相應的庫和 helper 放進系統中。可用ldd實現這個功能,如:
$ 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)
我們也提供了一個程序來自動完成這個工作,見文后參考。
對于klibc的,一般會有個庫文件,例如 /usr/lib/klibc/lib/klibc-KC4v-FjcUUw8mDjRL-kY8PS8U3E.so
將此文件放在相對initrd的根目錄的 /lib 目錄下即可。
需要的設備有:
mknod dev/console c 5 1
mknod dev/null c 1 3
# 如果為ramdisk,最好創建它
mknod dev/ram0 c 1 0
二.檢測存儲設備
早先有 devfs hotplug等來檢測,或是靜態創建加載模塊以支持存儲設備如硬盤,光盤等。
udev的出現使這個過程轉移到用戶空間,靈活性大大增強,使這個過程能自動完成。
對于新版的 udev (大概是>098),完成這一過程只需寫好相應的規則,有相應的模塊,執行以下命令即可自動加載模塊并創建相應的設備節點:
# 掛載所需文件系統
mount -t proc none /proc
mount -t sysfs none /sys
mount -t tmpfs -o size=10M none /dev
# 關閉內核消息打印
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
# 啟動 udev
/sbin/udevd --daemon
/sbin/udevtrigger
/sbin/udevsettle
三.檢測文件系統
由于各系統和內核的不同,掛載文件系統有時不像我們平時使用的那樣,會自動探測,可以使用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的方式來掛載文件系統,
mount -U
mount -L
四.交權給主系統的init
實現方法主要有 pivot_root 和 switch_root與 run-init。
在這之前需要對之前掛載的虛擬文件系統與dev轉移到主系統。
mount --move /dev /root/dev
mount --move /proc /root/proc
mount --move /sys /root/sys
1.pivot_root
傳統的做法是用 pivot_root,然而 pivot_root不會自動處理很多工作,如釋放initrd所有的內存,執行主系統的init。
使用方法如下:
cd /root
mkdir -p initrd
pivot_root . initrd
# 注意 /root 要是合法的設備掛載上的 如 /dev/sda1,如果不是這樣,將可能出現錯誤。
# 目標目錄也需要存在,其它參閱man page
exec chroot . sh -c 'umount /initrd; exec /sbin/init' dev/console 2>&1
# 如果不需要清理,也可簡單的運行
exec chroot . /sbin/init
2.switch_root/run-init
這兩者差別不大,前者是busybox提供的,后者是klibc的。有點區別的是后者可接受 - 開頭的參數。
用法:
cd /root
exec switch_root . /sbin/init
或
exec run-init . /sbin/init --debug
至此,initrd的使命完成。
轉自:
posted on 2009-11-19 16:54
chatler 閱讀(329)
評論(0) 編輯 收藏 引用 所屬分類:
Linux_SysAdmin