分享

看看initramfs加载的完整流程

 breezy 2015-07-06
                     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(".");
}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多