子系统的初始化

    技术2024-07-29  61

    6.2 子系统的初始化

    所以接下来说do_basic_setup函数,任然是来自init/main.c

     

    778/*

     779 * Ok, the machine is now initialized. None of the devices

     780 * have been touched yet, but the CPU subsystem is up and

     781 * running, and memory and process management works.

     782 *

     783 * Now we can finally start doing some real work..

     784 */

     785static void __init do_basic_setup(void)

     786{

     787        init_workqueues();

     788        cpuset_init_smp();

     789        usermodehelper_init();

     790        init_tmpfs();

     791        driver_init();

     792        init_irq_proc();

     793        do_ctors();

     794        do_initcalls();

     795}

     

    第一个函数init_workqueues初始化工作队列events每个CPU一个对工作队列机制感兴趣的同学请访问博客下半部分

    http://blog.csdn.net/yunsongice/archive/2010/03/07/5354011.aspx

     

    第二个函数cpuset_init_smp是个空函数因为没有配置CONFIG_CPUSETS

     

    第三个函数usermodehelper_init初始化工作队列khelper每个CPU一个。

     

    第四个函数init_tmpfs注册并安装tmpfs文件系统它的file_system_type结构如下:

    static struct file_system_type tmpfs_fs_type = {

           .owner           = THIS_MODULE,

           .name             = "tmpfs",

           .get_sb           = shmem_get_sb,

           .kill_sb    = kill_litter_super,

    };

     

    第五个函数driver_init建立设备驱动模型sysfsksetkobjectsubsystem结构并向其中注册cpu、内存和总线的驱动。

     

    第六个函数init_irq_proc/proc文件系统中增加子目录irq来显示中断描述符表中的所有元素。

     

    第七个函数do_ctors,不是太明白,忽略它。

     

    最后来说说第八个函数do_initcalls();

     

    首先说一下,内核中有一个专门的节,用来存放初始化末尾要被调用的函数。举个例子init/initramfs.c中的最后一句是:

    rootfs_initcall(populate_rootfs);

    而:

    #define rootfs_initcall(fn)             __define_initcall("rootfs",fn,rootfs)

    #define __define_initcall(level,fn,id) /

            static initcall_t __initcall_##fn##id __attribute_used__ /

            __attribute__((__section__(".initcall" level ".init"))) = fn

     

    可以看出来这个populate_rootfs被放在.initcallrootfs.init节中了。然后呢,这个节在链接的时候会被output.initcall.init节中 (为了避免交叉过多)。

     

    好了,在明白了initcall相关信息后,继续看看do_initcalls()

    它的本质就是

            for (call = __initcall_start; call < __initcall_end; call++)

                    result = (*call)();

     

    这样所有的initcall就会被调用了,这是为了避免把所有代码集合到一块的一个方法,虽然这带来了一个问题,谁在前谁在后?内核专门为这个大节分了一些类,哪些在前哪些在后连接的时候会安排的。

     

    正好,现在知道 populate_rootfs会被执行,它处理initfamfsinitrd,首先是执行unpack_to_rootfs,然后检查是否有initrd。代码就不列出来了。

     

    再回到调用do_basic_setup()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();

        }

     

    这段代码检查是否有必要mount根文件系统,如果vmlinuz中带有initfamfs,而且其中已经有init,那么就不这么做了(我现在工作用的目标系统就是这样的,里面有个init),否则的话内核还要mount init所在的(也是所有用户态进程的最除根文件系统)根文件系统,挂在根文件系统和执行initlinux启动过程最后要做的事情。

     

    好了,就不说mount_root的细节了,prepare_namespace还是很值得一看的,可以去翻源代码。如果它失败了,就是panic VFS no root found 这样的错误了。现在假设已经有了根文件系统了,这样就到了kernel_init中的最后一条函数调用了——init_post

     

    最新回复(0)