1. 程式人生 > >Linux驅動late_initcall和module_init相關分析

Linux驅動late_initcall和module_init相關分析

文章來源:http://blog.chinaunix.net/uid-29570002-id-4387097.html

Linux系統啟動過程很複雜,因為它既需要支援模組靜態載入機制也要支援動態載入機制。模組動態載入機制給系統提供了極大的靈活性,驅動程式既可支援靜態編譯進核心,也可以支援動態載入機制。Linux系統中對裝置和子系統的初始化在最後進行,主要過程可以用下圖表示。

1

進入子系統初始化時,在核心init程序中進行裝置初始化,最為複雜、詭異的機制莫過於do_initcalls()函式呼叫,該函式完成了所有需要靜態載入模組的初始化,需要進行靜態載入的核心模組,需要使用一些特定的巨集進行處理,下面詳細來說明一些

linux核心中initcalls機制。

先來看看do_initcalls()函式原型:

2

核心部分是639~671之間,該部分是一個函式指標呼叫,遍歷_initcall_start~_initcall_end範圍,逐個呼叫函式指標。

_initcall_start~_initcall_end之間存放的是什麼呢,可以以下面一幅示意圖來說明。

3

圖左邊是地址指標,右邊是相關巨集,使用相關巨集處理函式指標,可以將被處理的函式指標放在特定的指標範圍內。例如,網路裝置層初始化函式是net_dev_init(),定義在net/core/dev.c中,在該函式下方有條巨集處理subsys_initcall(net_dev_init),該巨集完成將

net_dev_init函式指標放在上圖中.initcall4.init段中,在do_initcalls()函式呼叫時,其處於_initcall_start~_initcall_end直接,所以net_dev_init()就這樣被呼叫了。

這種機制真是比較巧妙,也比較難以理解,設計初衷就是為了實現一個通用的啟動流程,使移植或擴充套件時,只需要對需要啟動載入的模組進行巨集處理即可。

下面來詳細瞭解這種機制的實現方法。

先說一說gcc對手動定位程式碼段位置的支援,_attribute_gcc的關鍵字,指示編譯器給符號設定特定屬性。編譯完成後輸入到連結器的是各個帶有符號表的檔案,連結器對各個檔案中符號進行重定位,

_attribute_在該階段進行處理,將指定符號放在連結生成檔案段中特定位置,不單隻指程式碼段,也包括資料段,如系統初始化中經常見到的_initdata,即將指定符號放到資料段特定位置。

當然,具體這些段是如何生成的,也是有檔案進行配置,即在連結配置檔案arch/xxx/vmlinux.ds.S.,如下

4

2.6.16核心中INITCALLS已直接被替換為

*(.initcall1.init)*(.initcall2.init)*(.initcall3.init)*(.initcall4.init)*(.initcall5.init)*(.initcall6.init)*(.initcall7.init)

這和圖3中的結構是對應的。接下來看看核心提供了哪些巨集定義用來處理特定函式指標和資料。在include/linux/init.h檔案中,包括各種常見的包裝。

#define __define_initcall(level,fn) \static initcall_t __initcall_##fn __attribute_used__ \    __attribute__((__section__(".initcall" level ".init"))) = fn#define core_initcall(fn)       __define_initcall("1",fn)#define postcore_initcall(fn)   __define_initcall("2",fn)#define arch_initcall(fn)       __define_initcall("3",fn)#define subsys_initcall(fn)     __define_initcall("4",fn)#define fs_initcall(fn)         __define_initcall("5",fn)#define device_initcall(fn)     __define_initcall("6",fn)#define late_initcall(fn)       __define_initcall("7",fn)

可以看出,核心為滿足不同初始化等級,設計了1~77個等級,不同等級初始化程式碼用對應的巨集進行處理,讀者可以對照上表進行理解一下。還有其它一些巨集,用於各種任務需求,如模組載入巨集module_init()module_exit(),其處理又略有不同,讀者可以自己理解一下。

總的來說,initcalls機制提供給核心開發者或驅動開發者一個介面,方便將自己的函式新增到核心初始化列表中,在核心初始化最後階段進行處理。

#define module_init(x) __initcall(x); #define __initcall(fn) device_initcall(fn)modul_init是屬於device_initcall,也就是說 late_initcall 還要在 module_init的後面。