Initrd启动和功能分析

Initrd启动和功能分析
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的使命完成。 
转自:
http://www.91linux.com/html/article/kernel/20090712/17508.html

你可能感兴趣的:(Initrd启动和功能分析)