module_init/subsys_initcall/postcore_initcall 执行顺序。

在看i2c_init的时候,发现
postcore_initcall(i2c_init);在modul_init之前肯定会执行的。
查看他与module_init的区别.
在init.h中定义:

ifndef MODULE

define postcore_initcall(fn) __define_initcall(“2”,fn,2)

define subsys_initcall(fn) __define_initcall(“4”,fn,4)

else

define postcore_initcall(fn) module_init(fn)

define subsys_initcall(fn) module_init(fn)

endif

这段代码就不注释了吧。
其中__define_initcall定义如下:

define __define_initcall(level,fn,id) \

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

define module_init(x) __initcall(x);

define __initcall(fn) device_initcall(fn)

define device_initcall(fn) __define_initcall(“6”,fn,6)

define __define_initcall(level,fn,id) \

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

最终我们看到的是module_init的真身:__define_initcall(level,fn,id),仔细推敲这个真身,知道这是个宏,它把传给module_init的函数名组装成以__initcall为前缀的、以6为后缀的函数名,并把这个函数定义到代码段.initcall6.init里面。

在代码段.initcall6.init里面?这函数躲在这里干嘛,啥时候才轮得到它出头啊!找到有此字符串的文件vmlinux.lds.h,相关代码如下所示:

postcore_initcall 展开后其实为:
static initcall_t initcall_fn2___used __attribute((section(“.initcall” “2” “.init”))) = fn
定义了一个section,段名字为initcall2.init
同理可知,模块中经常使用的module_init为initcall6.init
这些initcall都放在什么地方呢?
在vmlinux_lds.h中定义道:
#define INIT_CALLS_LEVEL(level) \
VMLINUX_SYMBOL(__initcall##level##_start) = .; \
*(.initcall##level##.init) \
*(.initcall##level##s.init) \
#define INIT_CALLS \
VMLINUX_SYMBOL(__initcall_start) = .; \
*(.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) \
VMLINUX_SYMBOL(__initcall_end) = .;
到此为止知道了,这些inintcall+数字+.init开头的段的名字都放在__initcall_start和__initcall_end段之间。
那么__initcall_start放在那里呢?不由自主的看了vmlinux.lds。
在arch/arm/kernel的vmlinux.lds中定义到:
__initcall_start = .; (.initcallearly.init) __initcall0_start = .; (.ini tcall0.init) (.initcall0s.init) __initcall1_start = .; (.initcall1.init) * (.initcall1s.init) __initcall2_start = .; (.initcall2.init) (.initcall2s.i nit) __initcall3_start = .; (.initcall3.init) (.initcall3s.init) __initcal l4_start = .; (.initcall4.init) (.initcall4s.init) __initcall5_start = .; (.initcall5.init) (.initcall5s.init) __initcallrootfs_start = .; (.initca llrootfs.init) (.initcallrootfss.init) __initcall6_start = .; (.initcall6. init) (.initcall6s.init) __initcall7_start = .; (.initcall7.init) (.initc all7s.init) __initcall_end = .;

在内核启动过程中,执行do_initcalls的时候,会依次执行上述段代码。
static void __init do_initcalls(void)
{
int level;

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

}
initcall_levels在init的main.c中定义:
static initcall_t *initcall_levels[] __initdata = {
__initcall0_start,
__initcall1_start,
__initcall2_start,
__initcall3_start,
__initcall4_start,
__initcall5_start,
__initcall6_start,
__initcall7_start,
__initcall_end,
};
结合arm/arm/kernel的vmlinux.lds文件,可以知道这个二维数组存放的位置、成员,大小。
由此知道系统在启动过程中的执行流程,当然这只是内核启动过程的一小小小部分。

发布了21 篇原创文章 · 获赞 5 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/linchuanzhi_886/article/details/44516497