Linux内核如何挂载rootfs,取决于u-boot的环境变量:
u-boot先load一个ramdisk.cpio.gz
initrd=${ramdisk_addr} root=/dev/ram rw rdinit=/etc/init.sh
kernel解压内嵌的.cpio
root=/dev/mmcblk1p2 rw rootfstype=ext4
总得来说,有3种挂载方式:
1 、initrd (CONFIG_BLK_DEV=y)有 rdinit
u-boot先从SD卡或EMMC load一个ramdisk.cpio.gz压缩包,内核启动时解压它,把它释放到rootfs(已注册)作为 / ,根据"rdinit="执行启动脚本。启动脚本会指引如何挂载真实的rootfs(在EMMC上的)。
2 、initramfs (CONFIG_BLK_DEV=y)无 /init
kernel启动时解压__initramfs_start段的.cpio压缩包(内核自动生成,只包含 / ,/dev , /root ,/dev/console),因为无/init,会执行挂载EMMC上的真实rootfs。
3 、default (CONFIG_BLK_DEV=n)
kernel 调用default_rootfs创建(/ , /dev , /root , /dev/console), 不解压.cpio
挂载流程
start_kernel()->
在RAM上建立 / ,挂载rootfs
vfs_caches_init()->
sysfs_init();
init_rootfs();
这一步会注册rootfs(如指定"root="则为ramfs)
init_mount_tree()->
mnt = vfs_kern_mount(&rootfs_fs_type, 0, “rootfs”, NULL);
set_fs_pwd(current->fs, &root);
set_fs_root(current->fs, &root);
释放内核中的ramfs.cpio到 /
内核在编译时会生产一个ramfs的压缩包,这个压缩包中包含 / /dev /root /dev/console,由默认gen_initramfs.sh文件生成,生成的 .cpio为 initramfs_data.cpio 。具体操作:
1 、从__initramfs_start段读出initramfs_data.cpio,释放到rootfs
2 、该压缩包已经包含 /dev /dev/console /root,由 default_cpio_list 配置
static int __init populate_rootfs(void)
{
/* Load the built in initramfs */
char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);
if (err)
panic("%s", err); /* Failed to decompress INTERNAL initramfs */
if (!initrd_start || IS_ENABLED(CONFIG_INITRAMFS_FORCE))
goto done;
if (IS_ENABLED(CONFIG_BLK_DEV_RAM))
printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");
else
printk(KERN_INFO "Unpacking initramfs...\n");
err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start);
if (err) {
printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);
#endif
}
done:
/*
* If the initrd region is overlapped with crashkernel reserved region,
* free only memory that is not part of crashkernel region.
*/
if (!do_retain_initrd && initrd_start && !kexec_free_initrd())
free_initrd_mem(initrd_start, initrd_end);
initrd_start = 0;
initrd_end = 0;
flush_delayed_fput();
return 0;
}
以上代码含两次 unpack_to_rootfs,第一次解压kernel内嵌的.cpio,第二次判断initrd_start是否为0,不为0则表示u-boot load了ramdisk,则会进行解压。
挂载磁盘(EMMC)上的rootfs
kernel_init_freeable->
如果 /init 不存在,执行prepare_namespace
if (init_eaccess(ramdisk_execute_command) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
}
void __init prepare_namespace(void)
{
if (root_delay) {
printk(KERN_INFO "Waiting %d sec before mounting root device...\n",
root_delay);
ssleep(root_delay);
}
wait_for_device_probe();
md_run_setup();
//root=是否mtd开头
if (saved_root_name[0]) {
root_device_name = saved_root_name;
if (!strncmp(root_device_name, "mtd", 3) ||
!strncmp(root_device_name, "ubi", 3)) {
mount_block_root(root_device_name, root_mountflags);
goto out;
}
ROOT_DEV = name_to_dev_t(root_device_name);
if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5;
}
//mount_initrd = 0 不执行
if (initrd_load())
goto out;
/* wait for any asynchronous scanning to complete */
if ((ROOT_DEV == 0) && root_wait) {
printk(KERN_INFO "Waiting for root device %s...\n",
saved_root_name);
while (driver_probe_done() != 0 ||
(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
msleep(5);
async_synchronize_full();
}
//进行实际文件系统挂载
mount_root();
out:
devtmpfs_mount(); //<1>
init_mount(".", "/", NULL, MS_MOVE, NULL); //<2>
init_chroot("."); //<3>
}
<1>挂载devtmpfs到 实际文件系统的/dev下(上面已切换至/root下,因此填dev,磁盘rootfs必有dev目录)
<2>将磁盘rootfs挂载至/下,相当于/root下的磁盘rootfs将RAM里的/覆盖
<3>将init进程的根目录切换至实际根文件系统的/