我们知道用户在制作rootfs的时候,最终rootfs会三种方式挂接到系统:
-
用户制作的根文件系统通过initramfs的方式与内核镜像打包在一起,这种情况下的rootfs内核会进行cpio压缩;
此时主要会通过rest_init->kernel_init->populate_rootfs将用户根文件系统释放到内核的rootfs中。 -
用户制作的根文件系统通过cpio的方式单独处理,不与内核镜像打包在一起;
仍然会通过rest_init->kernel_init->populate_rootfs路径,只不过这里会判断一下initrd_start是否被初始化过,它主要来源于dts的chosen节点解析的linux,initrd-start,如果initrd_start变量不为空,那么代表用户将根文件系统存放到内存的某个区域, -
用户制作的根文件系统以某种文件系统格式化,形成文件系统镜像,内核在启动时,进行挂载
prepare_namespace,主要针对格式化rootfs为某种文件系统的情况,主要通过挂载用户制作的rootfs根文件系统镜像
注:这里主要通过init_eaccess检查是否能访问到某个文件(如linuxrc),如果能访问到则表示已经通过populate_rootfs 释放到内核根目录/,则不会执行prepare_namespace,否则将执行prepare_namespace挂载用户根文件系统
注:参考 kernel启动流程-start_kernel的执行_8.cpio initrd解包
此处我们主要描述第2种情况下内核如何获取用户根文件系统的地址,下面主要描述这个过程:
我们知道在内核启动start_kenrel -> setup_arch时会调用setup_machine_fdt这个函数,就是在这个函数中提取了rootfs在内存的地址:
start_kernel
|--setup_arch
|--setup_machine_fdt->early_init_dt_scan
|--early_init_dt_scan_nodes
|--of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line)
|--early_init_dt_check_for_initrd(node)
|--prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len);
|--start = of_read_number(prop, len/4);
|--prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len);
|--end = of_read_number(prop, len/4)
|--phys_initrd_start = start;
|--phys_initrd_size = end - start;
我们看phys_initrd_start 这个地址是怎么被使用的?
setup_arch
|--arm64_memblock_init
|...
|--if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && phys_initrd_size)
| u64 base = phys_initrd_start & PAGE_MASK;
| u64 size = PAGE_ALIGN(phys_initrd_start + phys_initrd_size) - base;
| ....
|--if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && phys_initrd_size)
initrd_start = __phys_to_virt(phys_initrd_start)
initrd_end = initrd_start + phys_initrd_size;
phys_initrd_start 最终被赋值给全局initrd_start,通过拿到这个变量,在populate_rootfs时就可以将内存中的根文件系统释放到内核rootfs的根目录