linux initramfs,Linux INITRAMFS 与 INITRD「建议收藏」

2022-09-22 15:18:25 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

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

0 人点赞