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 阅读(318)
评论(0) 编辑 收藏 引用 所属分类:
Linux_SysAdmin