vim usr/initramfs_data.S SECTIONS { .init.ramfs : { *(.data) } // 包含所有data } .section .init.ramfs,"a" .incbin "usr/initramfs_data.cpio.gz" // data为usr/initramfs_data.cpio.gz文件内容 vim arch/arm/kernel/vmlinux.lds.S #ifdef CONFIG_BLK_DEV_INITRD . = ALIGN(32); __initramfs_start = .; usr/built-in.o(.init.ramfs) __initramfs_end = .; #endif vim init/initramfs.c rootfs_initcall(populate_rootfs); static int __init populate_rootfs(void) { // 先unpack built-in编译进内核的以__initramfs_start地址开始的fs数据 char *err = unpack_to_rootfs(__initramfs_start, __initramfs_end - __initramfs_start, 0); // 再unpack bootloader传过来的以initrd_start地址开始的fs数据 err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start, 0); } 生成initramfs.gz加载文件 scripts/gen_initramfs_list.sh -o /vobs/gliethttp/initramfs.gz /vobs/nfs/ 如果-d后面不加任何参数那么将显示默认的initramfs中包含的文件 scripts/gen_initramfs_list.sh -d 显示initramfs.cpio中的包含的文件 gunzip /vobs/gliethttp/initramfs.gz cpio -i -t < /vobs/gliethttp/initramfs 或者直接显示initramfs.gz中包含的文件 gunzip -c /vobs/gliethttp/initramfs.gz | cpio -i -t arch/arm/kernel/setup.c|730| paging_init(mdesc) start_kernel ==> setup_arch(&command_line) ==> paging_init ==> bootmem_init ==> bootmem_reserve_initrd(node) 会将phys_initrd_start后phys_initrd_size大小空间暂时保留,不被buddy回收管理[luther.gliethttp] 然后initrd_start = __phys_to_virt(phys_initrd_start)存放虚拟地址 start_kernel ==> vfs_caches_init ==> mnt_init ==> init_rootfs 位于fs/ramfs/inode.c中,调用register_filesystem(&rootfs_fs_type);注册rootfs 接下来 init_mount_tree()函数将调用mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);将rootfs挂在到/根目录下 然后使用如下两个函数,将后边建立线程的根目录指向rootfs, set_fs_pwd(current->fs, ns->root, ns->root->mnt_root); set_fs_root(current->fs, ns->root, ns->root->mnt_root); init/do_mounts.c|153| __setup("root=", root_dev_setup); 比如cmdline传入参数"root=/dev/ram0" static int __init root_dev_setup(char *line) { strlcpy(saved_root_name, line, sizeof(saved_root_name)); return 1; } __setup("root=", root_dev_setup); 在函数中将引用到saved_root_name arch/arm/kernel/vmlinux.lds.S|46| *(.init.setup) arch/x86/kernel/vmlinux_32.lds.S|131| *(.init.setup) __setup_start = .; *(.init.setup) __setup_end = .; 1. obsolete_checksetup 2. do_early_param start_kernel ==> parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, &unknown_bootoption); // 解析built-in的静态cmdline和静态param. ==> unknown_bootoption ==> obsolete_checksetup arch/arm/kernel/vmlinux.lds __start___param = .; *(__param) //为kernel buit in的内容,我的kernel没有定义该存储区段 __stop___param = .; start_kernel ==> parse_early_param ==*>parse_early_options ==**> parse_args("early options", cmdline, NULL, 0, do_early_param) // 使用do_early_param函数解析参数 ==> do_early_param 2.6.30.4内核cmdline常用命令行参数与相应处理函数 static int __init do_early_param(char *param, char *val) { struct obs_kernel_param *p; for (p = __setup_start; p < __setup_end; p++) { if ((p->early && strcmp(param, p->str) == 0) || (strcmp(param, "console") == 0 && strcmp(p->str, "earlycon") == 0) ) { if (p->setup_func(val) != 0) printk(KERN_WARNING "Malformed early option '%s'\n", param); } } /* We accept everything at this stage. */ return 0; } static int __init parse_tag_initrd(const struct tag *tag) { printk(KERN_WARNING "ATAG_INITRD is deprecated; " "please update your bootloader.\n"); phys_initrd_start = __virt_to_phys(tag->u.initrd.start); // 如果传入正确值,那么kernel就启动不起来 phys_initrd_size = tag->u.initrd.size; return 0; } __tagtable(ATAG_INITRD, parse_tag_initrd); start_kernel ==> rest_init ==> kernel_init static int __init kernel_init(void * unused) { lock_kernel(); set_cpus_allowed_ptr(current, cpu_all_mask); init_pid_ns.child_reaper = current; cad_pid = task_pid(current); smp_prepare_cpus(setup_max_cpus); do_pre_smp_initcalls(); start_boot_trace(); smp_init(); sched_init_smp(); do_basic_setup(); // 遍历module_init编译进内核的驱动初始化 // 其中就包括我们最关心的initramfs释放操作[luther.gliethttp] // populate_rootfs()会调用下面这个函数将initrd的内容释放到rootfs文件系统下unpack_to_rootfs #if 0 { struct obs_kernel_param *p; int i = 0; for (p = __setup_start; p < __setup_end; p++) { printk("[gliethttp _setup %03d] %s\n", i++, p->str); // 可以打印所有built-in的参数处理函数[luther.gliethttp] } } #endif if (!ramdisk_execute_command) // 先检查ramdisk_execute_command是否在rootfs目录下,即/根目录下存在, // 如果存在,那么不用执行prepare_namespace()[luther.gliethttp] ramdisk_execute_command = "/init"; if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { ramdisk_execute_command = NULL; prepare_namespace(); // 如果没有ramfs,那么尝试__setup("root=", root_dev_setup);用户传入的cmdline // 检查saved_root_name是否可挂载,如果可以那么sys_mount(".", "/", NULL, MS_MOVE, NULL); // 然后将主目录改变过去sys_chroot(".");. } init_post(); // 在init_post中,如果ramdisk_execute_command存在,那么启动用户空间的 // 指定的elf用户程序run_init_process(ramdisk_execute_command);[luther.gliethttp] // sys_open((const char __user *) "/dev/console", O_RDWR, 0); return 0; } ==> do_basic_setup ==*> do_initcalls调用所有arch/arm/kernel/vmlinux.lds.S中定义的INITCALLS模块,比如:使用module_init内建到zImage中的所有驱动模块 #define pure_initcall(fn) __define_initcall("0",fn,0) #define core_initcall(fn) __define_initcall("1",fn,1) #define core_initcall_sync(fn) __define_initcall("1s",fn,1s) #define postcore_initcall(fn) __define_initcall("2",fn,2) #define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s) #define arch_initcall(fn) __define_initcall("3",fn,3) #define arch_initcall_sync(fn) __define_initcall("3s",fn,3s) #define subsys_initcall(fn) __define_initcall("4",fn,4) #define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s) #define fs_initcall(fn) __define_initcall("5",fn,5) #define fs_initcall_sync(fn) __define_initcall("5s",fn,5s) #define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs) #define device_initcall(fn) __define_initcall("6",fn,6) #define device_initcall_sync(fn) __define_initcall("6s",fn,6s) #define late_initcall(fn) __define_initcall("7",fn,7) #define late_initcall_sync(fn) __define_initcall("7s",fn,7s) 上面我们关心的是rootfs_initcall定义, 在init/initramfs.c中具体实现如下: rootfs_initcall(populate_rootfs); populate_rootfs()会调用下面这个函数将initrd的内容释放到rootfs文件系统下 unpack_to_rootfs((char *)initrd_start,initrd_end - initrd_start, 0); 这样initrd的ramdisk的内容全部释放到了rootfs文件系统中,所以ramdisk下的所有目录结构将全部被克隆到rootfs文件系统下 ==> prepare_namespace void __init prepare_namespace(void) { if (saved_root_name[0]) { // 如果有__setup("root=", root_dev_setup);用户传入的cmdline,那么去出来看看 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; /* 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(100); async_synchronize_full(); } is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; if (is_floppy && rd_doload && rd_load_disk(0)) ROOT_DEV = Root_RAM0; mount_root(); out: sys_mount(".", "/", NULL, MS_MOVE, NULL); sys_chroot("."); } |
|