Linux內核中的初始化initcall_t

基於Linux 2.6.32

include\asm-generic\Vmlinux.lds.h中有如下定義:

這等於是安排了名稱如.initcall<level>[s].init的一系列section的順序,level越小,越靠前。

符號__initcall_start記錄了這片區域的開始,__initcall_end符號記錄了這片區域的結束。

__early_initcall_end則將這片區域更進一步分成了兩段。

  1. #define INITCALLS                           \  
  1.     *(.initcallearly.init)                      \  
  1.     VMLINUX_SYMBOL(__early_initcall_end) = .;           \  
  1.     *(.initcall0.init)                      \  
  1.     *(.initcall0s.init)                     \  
  1.     *(.initcall1.init)                      \  
  1.     *(.initcall1s.init)                     \  
  1.     *(.initcall2.init)                      \  
  1.     *(.initcall2s.init)                     \  
  1.     *(.initcall3.init)                      \  
  1.     *(.initcall3s.init)                     \  
  1.     *(.initcall4.init)                      \  
  2.     *(.initcall4s.init)                     \  
  3.     *(.initcall5.init)                      \  
  1.     *(.initcall5s.init)                     \  
  1.     *(.initcallrootfs.init)                     \  
  1.     *(.initcall6.init)                      \  
  1.     *(.initcall6s.init)                     \  
  1.     *(.initcall7.init)                      \  
  1.     *(.initcall7s.init)  
  2.   
  1. #define INIT_CALLS                          \  
  1.         VMLINUX_SYMBOL(__initcall_start) = .;           \  
  1.         INITCALLS                       \  
  1.         VMLINUX_SYMBOL(__initcall_end) = .;  

 

下面來看看,這些section裏面究竟是什麼內容。

以pci_driver_init函數爲例(位於drivers/pci/pci-driver.c)。

 

  1. static int __init pci_driver_init(void)  
  1. {  
  1.     return bus_register(&pci_bus_type);  
  1. }  
  1.   
  1.   
  1. postcore_initcall(pci_driver_init); 

 

可見函數下面跟了一個postcore_initcall宏的調用,追蹤宏的定義,得到如下代碼。

 

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

 

這樣的話,postcore_initcall(pci_driver_init);展開後,其實就是如下內容

 

  1. static initcall_t __initcall_pci_driver_init2 __attribute__((__used__)) \  
  1.     __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].initsection中去。

 

  1. #define pure_initcall(fn)       __define_initcall("0",fn,0)  
  1.   
  1. #define core_initcall(fn)       __define_initcall("1",fn,1)  
  1. #define core_initcall_sync(fn)      __define_initcall("1s",fn,1s)  
  1. #define postcore_initcall(fn)       __define_initcall("2",fn,2)  
  1. #define postcore_initcall_sync(fn)  __define_initcall("2s",fn,2s)  
  1. #define arch_initcall(fn)       __define_initcall("3",fn,3)  
  1. #define arch_initcall_sync(fn)      __define_initcall("3s",fn,3s)  
  1. #define subsys_initcall(fn)     __define_initcall("4",fn,4)  
  1. #define subsys_initcall_sync(fn)    __define_initcall("4s",fn,4s)  
  1. #define fs_initcall(fn)         __define_initcall("5",fn,5)  
  1. #define fs_initcall_sync(fn)        __define_initcall("5s",fn,5s)  
  1. #define rootfs_initcall(fn)     __define_initcall("rootfs",fn,rootfs)  
  1. #define device_initcall(fn)     __define_initcall("6",fn,6)  
  2. #define device_initcall_sync(fn)    __define_initcall("6s",fn,6s)  
  3. #define late_initcall(fn)       __define_initcall("7",fn,7)  
  4. #define late_initcall_sync(fn)      __define_initcall("7s",fn,7s)  

 

下面的問題來了,這些函數指針如何被調用呢?接着往下看。

內核初始化時,kernel_init函數(位於init/main.c)會依次執行到如下函數,完成這些初始化函數的調用。

可見,調用順序是,level越小的section中的函數指針,越先被調用。

 

  1. static void __init do_pre_smp_initcalls(void)  
  2. {  
  3.     initcall_t *call;  
  4.   
  5.     for (call = __initcall_start; call < __early_initcall_end; call++)  
  6.         do_one_initcall(*call);  
  7. }  

 

  1. static void __init do_initcalls(void)  
  2. {  
  3.     initcall_t *call;  
  4.   
  5.     for (call = __early_initcall_end; call < __initcall_end; call++)  
  6.         do_one_initcall(*call);  
  7.   
  8.     /* Make sure there is no pending stuff from the initcall sequence */  
  9.     flush_scheduled_work();  
  10. }  

 

源文檔 <http://blog.csdn.net/crazycoder8848/article/details/50847013

發佈了56 篇原創文章 · 獲贊 0 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章