Linux內核中的xx_initcall

本文編輯整理自: http://www.linuxidc.com/Linux/2011-09/43354.htm
先看這些宏的定義(定義在文件include/linux/init.h中)
#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) 
這些宏都用到了__define_initcall(),再看看它的定義(一樣定義在文件include/linux/init.h中)
#define __define_initcall(level, fn,id) \  
       static initcall_t __initcall_##fn##id __used
        __attribute__(( __section__(". initcall" level " .init"))) = fn
這其中 initcall_t 是函數指針,原型以下,
typedef int (*initcall_t)(void); 
而編譯器宏 __attribute__((__section__())) 則表示把對象放在一個這個由括號中的名稱所指代的section中。
__define_initcall("6",fn,6) 則是把fn的地址放到. initcall 6 . init 這個selection中
另外,注意這裏 static 的做用,只是當前編譯單元可見,從全局來看的 __initcall_##fn##id 完整名字,是要加當前編譯文件名(或稱當前編譯單位名)做爲前綴的
因此__define_initcall的含義是:
1) 聲明一個名稱爲__initcall_##fn的函數指針;
2) 將這個函數指針初始化爲fn;
3) 編譯的時候須要把這個函數指針變量放置到名稱爲 ". initcall " level " .init "的section中
明確了 __define_initcall 的含義,就知道了它實際上是分別將這些初始化標號修飾的函數指針放到各自的section中的。
SECTION「 .initcall level 」. init 」被放入 INITCALLS include/asm-generic/vmlinux.lds.h )
#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) 
VMLINUX_SYMBOL( __early_initcall_end) =  . ;   這裏的   . 是一個特殊的符號,它是定位器,一個位置指針,指向程序地址空間內的某位置(或某section內的偏移,若是它在SECTIONS命令內的某section描述內),該符號只能在SECTIONS命令內使用。
這句命令的意思,是對 __early_initcall_end 變量賦值爲 當前section內的偏移
*( .initcall7s.init ) 表示的是 全部輸入文件 中的名爲 .initcall7s.init SECTIONS
__initcall_start __initcall_end 以及 INITCALLS 中定義的SECTION都是在arch/xxx/kernel/vmlinux.lds.S中放在 .init 段的。
SECTIONS  
        . init : { 
               __initcall_start = .
                       INITCALLS  
                __initcall_end = .
        } 
}
. init 只是用於對 SECTIONS 進行分類 
__initcall_start = . __initcall_end = . 表示分別對變量 __initcall_start __initcall_end   賦值爲 當前section內的偏移。
而這些 SECTION 裏的函數在初始化時被順序執行(init內核線程-> do_basic_setup() [main.c#778]-> do_initcalls() )。
程序( init/main.c 文件 do_initcalls() 函數)以下, do_initcalls() XX.initcallXX.init 中的函數按順序都執行一遍。
for (call = __early_initcall_end ; call < _ _initcall_end ; call++) 
        do_one_initcall(*call); 
關於SECTION的更多內容請參考《 Linux下的lds連接腳本簡介
相關文章
相關標籤/搜索