module_init() 、core_initcall() 、 early_initcall()的对比与分析

标题的三个宏对应了内置模块的初始化顺序。
一般情况下,驱动使用device_initcall或者module_init。而early_initcall一般用更早,通常是驱动初始化前的一段时间,由初始化硬件子系统的部分所使用 。
在内核代码init/main.c,首先会有少量用于初始化特定架构的代码,这部分代码在arch/<arch>boot和arch/<arch>kernel中,初始化架构结束后,start_kernel函数就会被调用,然后文件内的另一个函数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();
}

这个函数是以do_initcall()为终结,我们将视线往上移动一点,

static initcall_t *initcall_levels[] __initdata = {
    __initcall0_start,
    __initcall1_start,
    __initcall2_start,
    __initcall3_start,
    __initcall4_start,
    __initcall5_start,
    __initcall6_start,
    __initcall7_start,
    __initcall_end,
};
/* Keep these in sync with initcalls in include/linux/init.h */
static char *initcall_level_names[] __initdata = {
    "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);
}
static void __init do_initcalls(void)
{
    int level;

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

    可以看见initcall_level_names的名称和其对应的索引:early是0, core是1等等。这其中每一个__initcall*_start都指向一个会被挨个调用的函数指针数组,这些函数指针实际上就是模块和内置的初始化函数,即使用module_initearly_initcall等指定的函数。
    那么是什么决定哪一个函数调用哪一个__initcall*_start数组呢?实际上是链接器使用module_init*_initcall宏进行的。对于内置模块,这些宏将函数指针分配和给定的ELF段。

Example with module_init

想象一个内置模块已经被在.config中被配置为了y,module_init会像include/linux/init.h中描述的那样被扩展

#define module_init(x) __initcall(x);

往下走

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

我们发现module_init(my_func)就意味__define_initcall(my_func,6)```, 也就是

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

所以,我们最终得到的是

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

看起来很多GCC的玩意,但是实际上最终效果就是创建了一个新的符号__initcall_my_func6,这个符号会放在名为.initcall6_init的ELF段中。然后我们还看到,指针指向了函数my_func。添加所有函数到这个这个段中最终创建了完整的函数指针数组,他们都存放于.initcall6.initELF段中。

Initialization example

代码中有这么一段

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

我们用level 6为例,所有内置模块使用module_init初始化。代码从__initcall6_start开始,它的值是在.initcall6.init段中第一个注册的函数指针地址,以__initcall7_start为中终点,每次都以*fn的大小进行累加,类型为initcall_t或者void*,大小是32bit或者64bit,这个是由架构决定的。
do_one_initcall会调用当前条目所指向的函数。
在一个特定的初始化段中,决定为什么一个初始化函数会比另外一个函数先调用的是在Makefile文件中,因为链接器会一个接一个的在各自的ELF init.section中链接__initcall__*符号。
目前内核使用的就是这样的方案,如下

# GPIO must come after pinctrl as gpios may need to mux pins etc
obj-y                           += pinctrl/
obj-y                           += gpio/
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,194评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,058评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,780评论 0 346
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,388评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,430评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,764评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,907评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,679评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,122评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,459评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,605评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,270评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,867评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,734评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,961评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,297评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,472评论 2 348

推荐阅读更多精彩内容