内核中 xxx_initcall 的调用过程分析

内核版本:linux-4.19

上一篇文章提到了这段代码:

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 调用的云雾就已经拨开了吧!

转载于:https://www.cnblogs.com/GyForever1004/p/10400276.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值