大家好,又见面了,我是你们的朋友全栈君。
initramfs文件生效的过程大致分为四步:
第一步:Kernel首先要注册一个RAMFS文件系统类型(实际注册的类型名称是”ROOTFS”,后续我们可以看到它实际上就是”RAMFS”);
第二步:然后加载(mount)一个空的rootfs文件系统,类型就是上面提到的RAMFS(ROOTFS);
第三步:寻址initramfs文件“XXX.cpio.gz”并解压到已mount的rootfs文件系统中;
第四步:寻址用户空间的init,并执行init进程;
第一步和第二步的调用堆栈如下:
init/main.c: start_kernel() ->
fs/dcache.c: vfs_caches_init() ->
fs/namespace.c: mnt_init() {
…
// The last 2 lines
init_rootfs();
init_mount_tree();
}
在“fs/ramfs/inode.c: init_rootfs()”函数中,注册了”ROOTFS”文件系统类型。通过阅读inode.c源码,发现”ROOTFS”就是”RAMFS”,几乎没有区别。
在“fs/namespace.c: init_mount_tree()”函数中,加载了一个类型为ROOTFS的空root:
mnt
= do_kern_mount(“rootfs”, 0, “rootfs”, NULL);
//第一个rootfs引用文件系统类型ROOTFS,即RAMFS。第二个rootfs为mount entry。
第三步(寻址initramfs文件XXX.cpio.gz,并解压)的调用堆栈如下:
kernel/init/main.c: start_kernel() {
…
vfs_caches_init();
…
//
The last line
rest_init();
}
kernel/init/main.c: rest_init() {
kernel_thread(kernel_init, NULL,
CLONE_FS | CLONE_SIGHAND);
…
}
kernel/init/main.c: kernel_init() {
…
do_basic_setup();
…
}
kernel/init/main.c: rest_init() {
…
do_initcalls();
}
// OK
kernel/init/initramfs.c:populate_rootfs() {
// Decompress the .gz into
‘rootfs’
}
rootfs_initcall(populate_rootfs);
检查populate_rootfs做了什么,它通过__initramfs_start指针和__initramfs_end指针访问XXX.cpio.gz文件,调用函数unpack_to_rootfs函数把源文件解压到rootfs中。gunzip执行解压过程,do_name()/do_copy/do_symlink/do_XXX为解压过程回调函数,在这些do_XXX回调函数中,看到大量使用sys_open/sys_write/sys_XXX函数创建、读写、更改文件和目录及其权限。sys_open/sys_write/sys_read实际上是用户空间open/write/read函数系统调用的实现。
至此,一个完整的rootfs已在目标系统的内存中。
第四步(寻址并运行用户空间INIT进程)的调用堆栈:
init/main.c: kernel_init() {
// At the bottom lines
if (!ramdisk_execute_command)
ramdisk_execute_command = “/init”;
}
kernel/init/main.c: init_post() {
if (ramdisk_execute_command) {
run_init_process(ramdisk_execute_command);
}
}
kernel/init/main.c: run_init_process() {
kernel_execve(init_filename,
argv_init, envp_init);
}
也就是说,只要initramfs文件“XXX.cpio.gz”的根目录下有一个有效的init,或者一个对init有效的链接,Kernel就能启动这个rootfs。那假如说initramfs的rootfs为空(CONFIG_INITRAMFS_SOURCE没有配置或者指向不存在的位置),或者initramfs的rootfs没有有效的“/init”文件。则Kernel会解析commandline,尝试从MTD/UBI之类的NAND分区启动,或者启动INITRAMDISK,或者NFS启动(如果开启了此功能),其调用堆栈如下:
kernel/init/main.c: kernel_init() {
// …
if (!ramdisk_execute_command)
ramdisk_execute_command =
“/init”;
if (sys_access((const char __user
*) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command =
NULL;
prepare_namespace();
}
// …
}
kernel/init/do_mounts.c:
prepare_namespace() {
// …
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;
}
if (initrd_load())
goto out;
// …
mount_root() {
// …
mount_nfs_root();
// …
}
// …
}
再来总结一下INITRAMFS的特点:
lROOTFS和RAMFS文件系统在同一个源码文件“inode.c”中实现,他们基本一致,通过Kernel文档也能说明这一点。同时,这也应证了配置Kernel的时候为什么只需要配置’CONFIG_INITRAMFS_SOURCE’一个选项足也。
lROOTFS文件系统类型的注册,以及rootfs的加载,initramfs的解压加载过程,都是Kernel启动过程的默认行为,menuconfig中没有任何选项可以开关。
l实际上rootfs不仅为INITRAMFS提供服务,它还为INITRAMDISK的加载提供中转服务,这一过程后面会讲到。
linitramfs默认启动“/init”,请确保在那个位置有一个有效的init或者是链接。
linitramfs的处理总是优先于对commandLine中启动参数的处理,因此initramfs和commandline无关。当最终的zImage包含了一个有效的XXX.cpio.gz时,不管bootloader传给kernel什么样的commandline,Kernel都会从initramfs启动,比如:
bootargs=root=/dev/mtdblock5 rootfstype=yaffs2 init=/init
console=ttySAC0,115200
//即使当前mtdblock5有一个完整的yaffs2文件系统并且其’/init’也有效’.当前Kernel也会尝试优先启动initramfs。
bootargs=console=ttySAC0,115200
//即使没有指定root/rootfstype/init选项,当前kernel也会尝试自带的initramfs。
l当initramfs没有有效的root时,Kernel才会根据commandline的配置,尝试从mtd/ubi等NAND分区,或者是从ramdisk设备文件,或者是从nfs服务器加载root。
3. Kernel 2.4 armnommu平台对INITRAMDISK的处理
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/169687.html原文链接:https://javaforall.cn