linux 3.7.2

init/main.c

asmlinkage void __init start_kernel(void) // 進入點

{

    lockdep_init();
    smp_setup_processor_id();
    debug_objects_early_init();

    /*
     * Set up the the initial canary ASAP:
     */
    boot_init_stack_canary();

    cgroup_init_early();

    local_irq_disable();
    early_boot_irqs_disabled = true;

/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
    tick_init();
    boot_cpu_init();
    page_address_init();

    printk(KERN_NOTICE "%s", linux_banner);  // 理論上kernel 第一行印出.....

..........................................

    /* Do the rest non-__init'ed, we're now alive */
    rest_init(); // 最後這行才會去init device.....

}

 

static noinline void __init_refok rest_init(void)

{

    rcu_scheduler_starting();
    /*
     * We need to spawn init first so that it obtains pid 1, however
     * the init task will end up wanting to create kthreads, which, if
     * we schedule it before we create kthreadd, will OOPS.
     */
    kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);   // kernel_init 是pfn

}

 

static int __ref kernel_init(void *unused)

{

    kernel_init_freeable();

......

}

static void __init kernel_init_freeable(void)

{

......

    do_basic_setup();   //

......

}

 


/*
 * Ok, the machine is now initialized. None of the devices
 * have been touched yet, but the CPU subsystem is up and
 * running, and memory and process management works.
 *
 * Now we can finally start doing some real work..
 */
static void __init do_basic_setup(void)
{
    cpuset_init_smp();
    usermodehelper_init();
    shmem_init();
    driver_init();
    init_irq_proc();
    do_ctors();
    usermodehelper_enable();
    do_initcalls();                   // 這裡才是真正執行initial device
}

 

static void __init do_initcalls(void)
{
    int level;

    for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
        do_initcall_level(level);
}


/* Keep these in sync with initcalls in include/linux/init.h */
static char *initcall_level_names[] __initdata = {             // 這是initcall level, 共0~7, 依序去執行
    "early",
    "core",
    "postcore",
    "arch",
    "subsys",
    "fs",
    "device",
    "late",
};

static void __init do_initcall_level(int level)
{
    extern const struct kernel_param __start___param[], __stop___param[];
    initcall_t *fn;

    strcpy(static_command_line, saved_command_line);
    parse_args(initcall_level_names[level],
           static_command_line, __start___param,
           __stop___param - __start___param,
           level, level,
           &repair_env_string);

    for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
        do_one_initcall(*fn);
}

 

在kernel driver/module常看到module_init(fun1), early_initcall(fun2)這種宣告方式

就是把 fun2 加到 early level 的 list,

呼叫同一 level 的 function 順序是在linkage加入的順序.....

ex:

以下參考: http://stackoverflow.com/questions/18605653/linux-module-init-vs-core-initcall-vs-early-initcall

以下的訊息得知module_init()最後是加到 "device" 這個level......

 

Example with module_init

Considering a built-in module (configured with y in .config), module_init simply expands like this (include/linux/init.h):

#define module_init(x)  __initcall(x);

and then we follow this:

#define __initcall(fn) device_initcall(fn)#define device_initcall(fn)             __define_initcall(fn,6)

So, now, module_init(my_func) means __define_initcall(my_func, 6). This is _define_initcall:

#define __define_initcall(fn, id) \
    staticinitcall_t __initcall_##fn##id __used \
    __attribute__((__section__(".initcall"#id ".init"))) = fn

which means, so far, we have:

staticinitcall_t __initcall_my_func6 __used
__attribute__((__section__(".initcall6.init")))= my_func;

Wow, lots of GCC stuff, but it only means that a new symbol is created, __initcall_my_func6, that's put in the ELF section named .initcall6.init, and as you can see, points to the specified function (my_func). Adding all the functions to this section eventually creates the complete array of function pointers, all stored within the .initcall6.init ELF section.

Initialization example

Look again at this chunk:

for(fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
    do_one_initcall(*fn);

Let's take level 6, which represents all the built-in modules initialized with module_init. It starts from __initcall6_start, its value being the address of the first function pointer registered within the .initcall6.init section, and ends at __initcall7_start (excluded), incrementing each time with the size of *fn (which is an initcall_t, which is a void*, which is 32-bit or 64-bit depending on the architecture).

do_one_initcall will simply call the function pointed to by the current entry.

Within a specific initialization section, what determines why an initialization function is called before another is simply the order of the files within the Makefiles since the linker will concatenate the __initcall_* symbols one after the other in their respective ELF init. sections.

This fact is actually used in the kernel, e.g. with device drivers (drivers/Makefile):

# GPIO must come after pinctrl as gpios may need to mux pins etc
obj-y                           += pinctrl/
obj-y                           += gpio/
arrow
arrow
    全站熱搜

    Orion 發表在 痞客邦 留言(0) 人氣()