基於Linux 2.6.32
include\asm-generic\Vmlinux.lds.h中有如下定義:
這等於是安排了名稱如.initcall<level>[s].init的一系列section的順序,level越小,越靠前。
符號__initcall_start記錄了這片區域的開始,__initcall_end符號記錄了這片區域的結束。
__early_initcall_end則將這片區域更進一步分成了兩段。
- #define INITCALLS \
- *(.initcallearly.init) \
- VMLINUX_SYMBOL(__early_initcall_end) = .; \
- *(.initcall0.init) \
- *(.initcall0s.init) \
- *(.initcall1.init) \
- *(.initcall1s.init) \
- *(.initcall2.init) \
- *(.initcall2s.init) \
- *(.initcall3.init) \
- *(.initcall3s.init) \
- *(.initcall4.init) \
- *(.initcall4s.init) \
- *(.initcall5.init) \
- *(.initcall5s.init) \
- *(.initcallrootfs.init) \
- *(.initcall6.init) \
- *(.initcall6s.init) \
- *(.initcall7.init) \
- *(.initcall7s.init)
- #define INIT_CALLS \
- VMLINUX_SYMBOL(__initcall_start) = .; \
- INITCALLS \
- VMLINUX_SYMBOL(__initcall_end) = .;
下面來看看,這些section裏面究竟是什麼內容。
以pci_driver_init函數爲例(位於drivers/pci/pci-driver.c)。
- static int __init pci_driver_init(void)
- {
- return bus_register(&pci_bus_type);
- }
- postcore_initcall(pci_driver_init);
可見函數下面跟了一個postcore_initcall宏的調用,追蹤宏的定義,得到如下代碼。
- #define postcore_initcall(fn) __define_initcall("2",fn,2)
- #define __define_initcall(level,fn,id) \
- static initcall_t __initcall_##fn##id __used \
- __attribute__((__section__(".initcall" level ".init"))) = fn
這樣的話,postcore_initcall(pci_driver_init);展開後,其實就是如下內容
- static initcall_t __initcall_pci_driver_init2 __attribute__((__used__)) \
- __attribute__((__section__(".initcall2.init"))) = pci_driver_init;
而initcall_t實際上是個函數指針類型,定義如下
typedef int (*initcall_t)(void);
這樣的話,上面的宏展開後,其實就是定義了一個變量__initcall_pci_driver_init2,他是一個函數指針,指向pci_driver_init。這個變量被鏈接到名爲.initcall2.init的section中。
當然,內核中,還有如下一些和postcore_initcall類似的宏(位於include/linux/init.h)。
調用這些宏,就會產生相應的函數指針變量,這些變量則被鏈接到相應的名稱如.initcall<level>[s].init的section中去。
- #define pure_initcall(fn) __define_initcall("0",fn,0)
- #define core_initcall(fn) __define_initcall("1",fn,1)
- #define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
- #define postcore_initcall(fn) __define_initcall("2",fn,2)
- #define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
- #define arch_initcall(fn) __define_initcall("3",fn,3)
- #define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
- #define subsys_initcall(fn) __define_initcall("4",fn,4)
- #define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
- #define fs_initcall(fn) __define_initcall("5",fn,5)
- #define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
- #define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
- #define device_initcall(fn) __define_initcall("6",fn,6)
- #define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
- #define late_initcall(fn) __define_initcall("7",fn,7)
- #define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
下面的問題來了,這些函數指針如何被調用呢?接着往下看。
內核初始化時,kernel_init函數(位於init/main.c)會依次執行到如下函數,完成這些初始化函數的調用。
可見,調用順序是,level越小的section中的函數指針,越先被調用。
- static void __init do_pre_smp_initcalls(void)
- {
- initcall_t *call;
- for (call = __initcall_start; call < __early_initcall_end; call++)
- do_one_initcall(*call);
- }
- static void __init do_initcalls(void)
- {
- initcall_t *call;
- for (call = __early_initcall_end; call < __initcall_end; call++)
- do_one_initcall(*call);
- /* Make sure there is no pending stuff from the initcall sequence */
- flush_scheduled_work();
- }
源文檔 <http://blog.csdn.net/crazycoder8848/article/details/50847013>