C語言 module_init函數(shù)與initcall案例詳解
module_init這個函數(shù)對做驅(qū)動的人來說肯定很熟悉,這篇文章用來跟一下這個函數(shù)的實現(xiàn)。
在include/linux/init.h里面有module_init的定義,自然,因為一個module可以在內(nèi)核啟動時自動加載進(jìn)內(nèi)核,也可以由我們手動在需要時加載進(jìn)內(nèi)核,基于這種場景,內(nèi)核使用了MODULE這個宏,見代碼:
#ifndef MODULE #ifndef __ASSEMBLY__ ... #define __define_initcall(level,fn,id) \ static initcall_t __initcall_##fn##id __attribute_used__ \ __attribute__((__section__(".initcall" level ".init"))) = fn #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(fn) device_initcall(fn) #define module_init(x) __initcall(x); #else /* MODULE */ ... #define module_init(initfn) \ static inline initcall_t __inittest(void) \ { return initfn; } \ int init_module(void) __attribute__((alias(#initfn)));...
當(dāng)我們使用make menuconfig來配置內(nèi)核時,將某個module配置為m時,MODULE這個宏就被定義了,而當(dāng)配置為y時,則沒有定義,具體的實現(xiàn)在kernel的根Makefile(-DMODULE)里。
現(xiàn)在我們先看下第一種情況,即把module配置為m的情況,即else分支的代碼。
先看下initcall_t的定義:
typedef int (*initcall_t)(void);
它是一個接收參數(shù)為void, 返回值為int類型的函數(shù)指針。這樣就明白了,其實前兩句話只是做了一個檢測,當(dāng)你傳進(jìn)來的函數(shù)指針的參數(shù)和返回值與initcall_t不一致時,就會有告警。
重點在第三句,是使用alias將initfn變名為init_module,我們知道,kernel 2.4版本之前都是用init_module來加載模塊的。這樣做應(yīng)該是為了不用修改load module的那塊代碼吧。
當(dāng)我們調(diào)用insmod將module加載進(jìn)內(nèi)核時,會去找init_module作為入口地址,即是我們的initfn, 這樣module就被加載了。
取nvme.ko為例,我們可以通過objdump -t nvme.ko 查看該模塊的符號表,發(fā)現(xiàn)init_module和nvme_init指向同一個偏移量。如下:
現(xiàn)在看第二種情況,即我們選擇將模塊編進(jìn)內(nèi)核,讓它隨內(nèi)核啟動而加載。
這種情況下module_init最終會調(diào)用__define_initcall宏,這個宏的作用就是將我們的初始化函數(shù)放在".initcall" level ".init"中。
在這里是.initcall6.init, 它的位置可以在Vmlinux.lds.h里面找到:
#define INITCALLS \ *(.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)
而INITCALL可以在vmlinux.lds.S里面找到:
.init.text : AT(ADDR(.init.text) - LOAD_OFFSET) { __init_begin = .; _sinittext = .; *(.init.text) _einittext = .; } .init.data : AT(ADDR(.init.data) - LOAD_OFFSET) { *(.init.data) } . = ALIGN(16); .init.setup : AT(ADDR(.init.setup) - LOAD_OFFSET) { __setup_start = .; *(.init.setup) __setup_end = .; } .initcall.init : AT(ADDR(.initcall.init) - LOAD_OFFSET) { __initcall_start = .; INITCALLS __initcall_end = .; } .con_initcall.init : AT(ADDR(.con_initcall.init) - LOAD_OFFSET) { __con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .; }
上面貼出來的代碼是系統(tǒng)啟動時存放初始化數(shù)據(jù)的地方,執(zhí)行完成后不再需要,會被釋放掉。根據(jù)上面的內(nèi)存布局,可以列出初始化宏和內(nèi)存的對應(yīng)關(guān)系:
_init_begin ------------------- | .init.text | ---- __init |-------------------| | .init.data | ---- __initdata _setup_start |-------------------| | .init.setup | ---- __setup_param __initcall_start |-------------------| | .initcall1.init | ---- core_initcall |-------------------| | .initcall2.init | ---- postcore_initcall |-------------------| | .initcall3.init | ---- arch_initcall |-------------------| | .initcall4.init | ---- subsys_initcall |-------------------| | .initcall5.init | ---- fs_initcall |-------------------| | .initcall6.init | ---- device_initcall |-------------------| | .initcall7.init | ---- late_initcall __initcall_end |-------------------| | | | ... ... ... | | | __init_end -------------------
而各個initcall被調(diào)用的地方在kernel_init-》do_basic_setup-》do_initcalls里面:
static void __init do_initcalls(void) { initcall_t *call; int count = preempt_count(); for (call = __initcall_start; call < __initcall_end; call++) { ktime_t t0, t1, delta; char *msg = NULL; char msgbuf[40]; int result; if (initcall_debug) { printk("Calling initcall 0x%p", *call); print_fn_descriptor_symbol(": %s()", (unsigned long) *call); printk("\n"); t0 = ktime_get(); } result = (*call)(); ... }
到此這篇關(guān)于C語言 module_init函數(shù)與initcall案例詳解的文章就介紹到這了,更多相關(guān)C語言 module_init函數(shù)與initcall內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 深入剖析Android中init進(jìn)程實現(xiàn)的C語言源碼
- 詳解C語言用malloc函數(shù)申請二維動態(tài)數(shù)組的實例
- C語言中二維數(shù)組作為函數(shù)參數(shù)來傳遞的三種方法
- C語言函數(shù)傳遞數(shù)組和傳遞地址的區(qū)別你知道嗎
- VS2017開發(fā)C語言出現(xiàn)“no_init_all“的解決辦法
- C語言的數(shù)組指針與函數(shù)指針詳解
- C語言全方位講解指針與地址和數(shù)組函數(shù)堆空間的關(guān)系
- C語言創(chuàng)建數(shù)組實現(xiàn)函數(shù)init,empty,reverse
相關(guān)文章
深入了解C++ 結(jié)構(gòu)體(struct)與共用體(union)
這篇文章主要介紹了C++ 結(jié)構(gòu)體與共用體的的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)c++,感興趣的朋友可以了解下2020-08-08C語言判斷數(shù)是否為素數(shù)與素數(shù)輸出
大家好,本篇文章主要講的是C語言判斷數(shù)是否為素數(shù)與素數(shù)輸出,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12C語言編程數(shù)據(jù)結(jié)構(gòu)帶頭雙向循環(huán)鏈表全面詳解
這篇文章主要為大家介紹了C語言編程的數(shù)據(jù)結(jié)構(gòu)中帶頭雙向循環(huán)鏈表全面詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助祝大家多多進(jìn)步,早日升職加薪2021-10-10