###1. initrd处理流程
从linux 2.4到linux 2.6,inird发展了不同的版本, kernel启动时,对initrd的处理流程也不相同。
###2. linux 2.4对initrd的处理
1.bootloader 将 kernel 以及 /dev/initrd(由bootloader初始化,存放initrd) 中的内容加载到内存
2.在内核初始化的过程中,将 /dev/initrd 中的内容解压缩并拷贝到 /dev/ram0 (ramdisk)上
3.内核以刻度写的方式将 /dev/ram0 挂载为原始的根文件系统
4.如果 /dev/ram0 被指定为真正的根文件系统,那么内核跳至最后一步正常启动
5.执行 initrd 上的 /linuxrc 文件,linuxrc 通常是一个脚本文件,负责加载内核访问根文件系统必须的驱动, 以及加载根文件系统
6./linuxrc 执行完毕,真正的根文件系统被挂载
7.如果真正的根文件系统存在 /initrd 目录,那么 /dev/ram0 将从 / 移动到 /initrd。否则如果 /initrd 目录不存在, /dev/ram0 将被卸载
8.在真正的根文件系统上进行正常启动过程 ,执行 /sbin/init。 linux2.4 内核的 initrd 的执行是作为内核启动的一个中间阶段,也就是说 initrd 的 /linuxrc 执行以后,内核会继续执行初始化代码,我们后面会看到这是 linux2.4 内核同 2.6 内核的 initrd 处理流程的一个显著区别
###3. linux 2.6对initrd的处理流程
linux2.6 内核支持两种格式的 initrd,一种是 linux2.4 内核那种传统格式的文件系统镜像-image-initrd,它的制作方法同 Linux2.4 内核的 initrd 一样,其核心文件就是 “/linuxrc”,另外一种格式的 initrd 是 cpio 格式的,这种格式的 initrd 从 linux2.5 起开始引入,使用 cpio 工具生成,其核心文件不再是 “/linuxrc”,而是 “/init”,本文将这种 initrd 称为 cpio-initrd。尽管 linux2.6 内核对 cpio-initrd和 image-initrd 这两种格式的 initrd 均支持,但对其处理流程有着显著的区别,下面分别介绍 linux2.6 内核对这两种 initrd 的处理流程。
####3.1 初始化rootfs
先从古老的start_kernel() 函数说起。
asmlinkage void __init start_kernel(void)
{
......
[1] vfs_caches_init(totalram_pages);
......
[2] rest_init();
......
}
void __init vfs_caches_init(unsigned long mempages)
{
......
[3] mnt_init();
......
}
void __init mnt_init(void)
{
......
[4] init_rootfs();
[5] init_mount_tree();
}
int __init init_rootfs(void)
{
......
[6] err = register_filesystem(&rootfs_fs_type);
......
}
static void __init init_mount_tree(void)
{
......
[7] mnt = vfs_kern_mount(type, 0, "rootfs", NULL);
......
[8] set_fs_pwd(current->fs, &root);
[9] set_fs_root(current->fs, &root);
}
- 代码[1]: 调用vfs_caches_init来初始化虚拟内存系统
- 代码[6]: [1] -> [3] -> [4] -> [6] 注册类型为rootfs(其实就是RAMFS),名为rootfs的文件系统, rootfs是一个基于内存的文件系统,是linux在初始化时加载的第一个文件系统。
- 代码[7]: [1] -> [3] -> [5] -> [7] 挂载上一步注册的rootfs到根目录“/”(该函数会为VFS系统建立根目录“/”)
- 代码[8]: [1] -> [3] -> [5] -> [8] 将”/”目录设置为当前进程的当前目录(会被其fork出的子进程所继承)
- 代码[9]: [1] -> [3] -> [5] -> [9] 将”/”目录设置为当前进程的根目录(会被其fork出的子进程所继承)
####3.2 解压缩initrd
static noinline void __init_refok rest_init(void)
{
[10] kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
......
[11] pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
}
static int __ref kernel_init(void *unused)
{
[12] kernel_init_freeable();
......
}
static noinline void __init kernel_init_freeable(void)
{
......
[13] do_basic_setup();
......
}
static void __init do_basic_setup(void)
{
......
[14] do_initcalls();
......
}
static void __init do_initcalls(void)
{
......
[15] for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level);
}
rootfs_initcall(populate_rootfs);
代码[15]: [2] -> [10] -> [12] -> [13] -> [14] -> [15] 调用各个level的module_init函数, 包括initramfs的初始化函数populate_rootfs()
static int __init populate_rootfs(void)
{
[16] char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);
if (err)
panic(err); /* Failed to decompress INTERNAL initramfs */
[17] if (initrd_start) {
#ifdef CONFIG_BLK_DEV_RAM
int fd;
printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");
[18] err = unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start);
if (!err) {
free_initrd();
goto done;
} else {
clean_rootfs();
[19] unpack_to_rootfs(__initramfs_start, __initramfs_size);
}
printk(KERN_INFO "rootfs image is not initramfs (%s)"
"; looks like an initrd\n", err);
fd = sys_open("/initrd.image",
O_WRONLY|O_CREAT, 0700);
if (fd >= 0) {
[20] sys_write(fd, (char *)initrd_start,
initrd_end - initrd_start);
sys_close(fd);
free_initrd();
}
done:
#else
printk(KERN_INFO "Unpacking initramfs...\n");
[21] err = unpack_to_rootfs((char *)initrd_start,
initrd_end - initrd_start);
if (err)
printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);
free_initrd();
#endif
load_default_modules();
}
return 0;$
}
- 代码[16]: 假设initrd和内核链接在一起(内核的”.init.ramfs“数据段),将其解压到rootfs中。
- 代码[17]: 判断是否有单独的initrd文件,无论是cpio-initrd还是image-initrd,bootloader都会把initrd加载到init_start处
- 代码[18]: 试图把initrd文件作为initramfs解压到rootfs, 如果成功则跳到代码[21]
- 代码[19]: [18]失败, 将内核的”.init.ramfs“数据段解压缩到rootfs
- 代码[20]: 单独的initrd文件是image-initrd, 将其写入到“/initrd.image”文件中
继续看 __init kernel_init_freeable()
static noinline void __init kernel_init_freeable(void)
{
......
[13] do_basic_setup();
......
[22] if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";
[23] if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
}
}
- 代码[22]: 当cmdline中存在”rdinit=xxx“时, ramdisk_execute_command才会被设置, 否则将“/init”设置为第一个要执行的程序
- 代码[23]: 检查是否能获取”/init”,对于cpio-initrd, 第一个执行的程序为”/init“, 对于image-initrd, 第一个执行的程序为”/linuxrc”, 通过这个也能够判断使用的是image-initrd还是cpio-initrd,对于image-initrd,调用prepare_namespace()进一步处理
###4. image-initrd额外的处理步骤
void __init prepare_namespace(void)
{
......
[24] if (initrd_load())
goto out;
......
}
int __init initrd_load(void)
{
if (mount_initrd) {
[25] create_dev("/dev/ram", Root_RAM0);
/*
* Load the initrd data into /dev/ram0. Execute it as initrd
* unless /dev/ram0 is supposed to be our actual root device,
* in that case the ram disk is just set up here, and gets
* mounted in the normal path.
*/
if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {
sys_unlink("/initrd.image");
[26] handle_initrd();
return 1;
}
}
[27] sys_unlink("/initrd.image");
return 0;
}
- 代码[25]: 创建”/dev/ram0”
- 代码[26]: 如果cmdline传递了正确的root=/dev/ramx的话, 说明你将“/dev/ram0”作为真正的根设备(有些嵌入式设备就这么做)作为真正的文件系统来处理, 否则,作为中间过度的文件系统, 此时,调用handle_initrd()来处理, 而在 handle_initrd()中会执行“/linuxrc”, 并挂载正真的文件系统.
- 代码[27]: 删除”/initrd.img”文件.
###5. cpio-initrd和image-initrd的处理步骤
static int __ref kernel_init(void *unused)
{
[12] kernel_init_freeable();
......
[28] if (ramdisk_execute_command) {
if (!run_init_process(ramdisk_execute_command))
return 0;
pr_err("Failed to execute %s\n", ramdisk_execute_command);
}
[29] if (execute_command) {
if (!run_init_process(execute_command))
return 0;
pr_err("Failed to execute %s. Attempting defaults...\n",
execute_command);
}
[30] if (!run_init_process("/sbin/init") ||
!run_init_process("/etc/init") ||
!run_init_process("/bin/init") ||
!run_init_process("/bin/sh"))
return 0;
[31] panic("No init found. Try passing init= option to kernel. "
"See Linux Documentation/init.txt for guidance.");
}
- 代码[28]:如果 ramdisk_execute_command(cmdline中使用‘rdinit=xxx指定’)存在且执行成功则返回
- 代码[29]:如果 execute_command(cmdline中使用’init=xxx指定’)存在且执行成功则返回
- 代码[30]:如果 “/sbin/init”, “/etc/init”, “/bin/init”, “/bin/sh”中有任一个存在且执行成功则返回
- 代码[31]:如果上述命令都不存在或执行失败,则打出“panic”信息,并hang住系统
###6. image-initrd和cpio-initrd的处理差异
1.cpio-initrd并没有使用额外的ramdisk,而是将其内容输入到rootfs中,其实rootfs本身也是一个基于内存的文件系统。这样就省掉了ramdisk的挂载、卸载等步骤。 2.cpio-initrd启动完/init进程,内核的任务就结束了,剩下的工作完全交给/init处理;而对于image-initrd,内核在执行完/linuxrc进程后,还要进行一些收尾工作,并且要负责执行真正的根文件系统的/sbin/init。cpio-initrd不再象image-initrd那样作为linux内核启动的一个中间步骤,而是作为内核启动的终点,内核将控制权交给cpio-initrd的/init文件后,内核的任务就结束了,所以在/init文件中,我们可以做更多的工作,而不比担心同内核后续处理的衔接问题.