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文件,可以知道這個二維數組存放的位置、成員,大小。
由此知道系統在啓動過程中的執行流程,當然這只是內核啓動過程的一小小小部分。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章