?
?? 對 靜態編入內核的代碼和數據來說,當內核引導時, do_basic_setup()函數調用do_initcalls()函數,后者負責所有.init節函數的執行。
所有標識為__init的函數在鏈接的時候都放在.init.text這個區段內,
在這個區段中,函數的擺放順序是和鏈接的順序有關的,是不確定的。
2)
所有的__init函數在區段.initcall.init中還保存了一份函數指針,
在初始化時內核會通過這些函數指針調用這些__init函數指針,
并在整個初始化完成后,釋放整個init區段(包括.init.text,.initcall.init等),
注意,這些函數在內核初始化過程中的調用順序只和這里的函數指針的順序有關,
和1)中所述的這些函數本身在.init.text區段中的順序無關。
在2.4內核中,這些函數指針的順序也是和鏈接的順序有關的,是不確定的。
在2.6內核中,initcall.init區段又分成7個子區段,分別是
.initcall1.init
.initcall2.init
.initcall3.init
.initcall4.init
.initcall5.init
.initcall6.init
.initcall7.init
(參見include/linux/init.h和vmlinux.lds )
當需要把函數fn放到.initcall1.init區段時,只要聲明
core_initcall(fn);
即可。
其他的各個區段的定義方法分別是:
core_initcall(fn) --->.initcall1.init
postcore_initcall(fn) --->.initcall2.init
arch_initcall(fn) --->.initcall3.init
subsys_initcall(fn) --->.initcall4.init
fs_initcall(fn) --->.initcall5.init
device_initcall(fn) --->.initcall6.init
late_initcall(fn) --->.initcall7.init
而與2.4兼容的initcall(fn)則等價于device_initcall(fn)。
各個子區段之間的順序是確定的,即先調用.initcall1.init中的函數指針
再調用.initcall2.init中的函數指針,等等。
而在每個子區段中的函數指針的順序是和鏈接順序相關的,是不確定的。
在內核中,不同的init函數被放在不同的子區段中,因此也就決定了它們的調用順序。
這樣也就解決了一些init函數之間必須保證一定的調用順序的問題。
2. Linux Kernel源代碼中與段有關的重要宏定義
A. 關于__init、__initdata、__exit、__exitdata及類似的宏
打開Linux Kernel源代碼樹中的文件:include/init.h,可以看到有下面的宏定議:
#define __init? __attribute__ ((__section__ (".init.text")))? __cold
#define __initdata??? __attribute__ (( __section__ (".init.data")))
#define __exitdata?? __attribute__ (( __section__ (".exit.data")))
#define __exit_call? __attribute_used__ __attribute__ (( __section__ (".exitcall.exit")))
#define __init_refok? oninline __attribute__ ((__section__ (".text.init.refok")))
#define __initdata_refok __attribute__ ((__section__ (".data.init.refok")))
#define __exit_refok noinline __attribute__ ((__section__ (".exit.text.refok")))
.........
#ifdef MODULE
#define __exit? __attribute__ (( __section__ (".exit.text"))) __cold
#else
#define __exit __attribute_used__ __attribute__ ((__section__ (".exit.text"))) __cold
#endif
對于經常寫驅動模塊或翻閱Kernel源代碼的人,看到熟悉的宏了吧:__init, __initdata, __exit, __exitdata。
__init 宏最常用的地方是驅動模塊初始化函數的定義處,其目的是將驅動模塊的初始化函數放入名叫.init.text的輸入段。當內核啟動完畢后,這個段中的內存會被釋放掉供其他使用。
__initdata宏用于數據定義,目的是將數據放入名叫.init.data的輸入段。其它幾個宏也類似。
另外需要注意的是,在以上定意中,用__section__代替了section。還有其它一些類似的宏定義,這里不一一列出,其作用都是類似的。
?
模塊加載分為動態加載和靜態加載。
所謂靜態加載就是,開機加載系統時將模塊加載上去,這就是編譯進內核。
而動態加載就是在開機以后將模塊加載上去,這就是編譯成模塊!
?
init_module是默認的模塊的入口,如果你想指定其他的函數作為模塊的入口就需要
module_init函數來指定,比如
module_init ? (your_func);
其中your_func是你編寫的一個函數的名稱.
?
init_module()是真正的入口,module_init是宏,如果在模塊中使用,最終還是要轉換到init_module()上。
如果不是在模塊中使用,module_init可以說沒有什么作用。總之,使用module_init方便代碼在模塊和非模塊間移植。