天天看點

xxx_initcall 的調用

上一篇文章提到了這段代碼:

arch_initcall_sync(of_platform_default_populate_init);           

它的功能是完成 device_node 到 platform_device 的轉換。這篇文章就來大概的分析一下,它是怎樣被調用的。

arch_initcall_sync 定義如下:

#define ___define_initcall(fn, id, __sec) \
    static initcall_t __initcall_##fn##id __used \
        __attribute__((__section__(#__sec ".init"))) = fn;

#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)

#define arch_initcall_sync(fn)      __define_initcall(fn, 3s)           

根據 LDS 檔案的定義,會将這些 data 存儲在指定的位置:

#define INIT_CALLS_LEVEL(level)                     \
        __initcall##level##_start = .;              \
        KEEP(*(.initcall##level##.init))            \
        KEEP(*(.initcall##level##s.init))           \

#define INIT_CALLS                          \
    __initcall_start = .;                   \
    KEEP(*(.initcallearly.init))                \
    INIT_CALLS_LEVEL(0)                 \
    INIT_CALLS_LEVEL(1)                 \
    INIT_CALLS_LEVEL(2)                 \
    INIT_CALLS_LEVEL(3)                 \
    INIT_CALLS_LEVEL(4)                 \
    INIT_CALLS_LEVEL(5)                 \
    INIT_CALLS_LEVEL(rootfs)                \
    INIT_CALLS_LEVEL(6)                 \
    INIT_CALLS_LEVEL(7)                 \
    __initcall_end = .;            

在核心中,想要調用到這些資料,就會用到

__initcall##level##_start = .;           

這個辨別。

資料的位置已經搞定了,那麼核心又是怎樣調用的呢?接下來找到這些函數調用。

調用流程如下:

start_kernel
    -->rest_init
        -->kernel_thread(kernel_init, NULL, CLONE_FS);
            -->kernel_init
                -->kernel_init_freeable
                    -->do_basic_setup
                        -->do_initcalls
                            -->do_initcall_level           

do_initcall_level 代碼如下:

static void __init do_initcall_level(int level)
{
    initcall_entry_t *fn;

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

    trace_initcall_level(initcall_level_names[level]);
    for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
        do_one_initcall(initcall_from_entry(fn));
}           

根據下表,分别調用 do_one_initcall 來執行每一個 initcall。

static initcall_entry_t *initcall_levels[] __initdata = {
    __initcall0_start,
    __initcall1_start,
    __initcall2_start,
    __initcall3_start,
    __initcall4_start,
    __initcall5_start,
    __initcall6_start,
    __initcall7_start,
    __initcall_end,
};           

表中的 xxx_start 恰恰就是前面所提到的存儲辨別,那麼這些調用就聯系到了一起,實作函數的調用。

分析到這裡,相信關于 xxx_initcall 調用的雲霧就已經撥開了吧!

繼續閱讀