Linux是單內(nèi)核系統(tǒng),可通用計(jì)算平臺(tái)的外圍設(shè)備是頻繁變化的,不可能將所有的(包括將來(lái)即將出現(xiàn)的)設(shè)備的驅(qū)動(dòng)程序都一次性編譯進(jìn)內(nèi)核,為了解決這個(gè)問(wèn)題,Linux提出了可加載內(nèi)核模塊(Loadable Kernel Module,LKM)的概念,允許一個(gè)設(shè)備驅(qū)動(dòng)通過(guò)模塊加載的方式,在內(nèi)核運(yùn)行起來(lái)之后"融入"內(nèi)核,加載進(jìn)內(nèi)核的模塊和本身就編譯進(jìn)內(nèi)核的模塊一模一樣。
一個(gè)程序在編譯的地址的相對(duì)關(guān)系就已經(jīng)確定了,運(yùn)行的時(shí)候只是進(jìn)行簡(jiǎn)單的偏移,為了使模塊加載進(jìn)內(nèi)核后能夠被放置在正確的地址,并正確的調(diào)用系統(tǒng)的運(yùn)行的導(dǎo)出符號(hào)表,編譯模塊的時(shí)候必須要使用系統(tǒng)的編譯地址,并調(diào)用系統(tǒng)編譯出得靜態(tài)的導(dǎo)出符號(hào)表。即模塊必須使用系統(tǒng)的配置環(huán)境:Makefile+.config,一旦這兩個(gè)文件任意一個(gè)發(fā)生了變化,都很可能導(dǎo)致模塊的編譯地址與系統(tǒng)的編譯地址不匹配,造成運(yùn)行時(shí)的錯(cuò)誤甚至宕機(jī)。

導(dǎo)出符號(hào)表

從提供系統(tǒng)運(yùn)行效率的角度,一個(gè)模塊不是也不應(yīng)該是完全獨(dú)立的,即一個(gè)模塊往往會(huì)調(diào)用其他模塊提供的功能來(lái)實(shí)現(xiàn)自己的功能,這樣做能更好實(shí)現(xiàn)系統(tǒng)的分工并提高效率。Linux為了實(shí)現(xiàn)模塊間的相互調(diào)用,設(shè)計(jì)了導(dǎo)出符號(hào)表,每個(gè)模塊都可以將自己的一個(gè)私有的標(biāo)號(hào)導(dǎo)出到系統(tǒng)層級(jí),以使該標(biāo)號(hào)對(duì)其他模塊可見(jiàn),系統(tǒng)在編譯一個(gè)模塊的時(shí)候會(huì)自動(dòng)導(dǎo)出這個(gè)模塊的導(dǎo)出符號(hào)表到modules.syms文件(如果沒(méi)有導(dǎo)出任何符號(hào),可以為空),并在加載一個(gè)模塊的時(shí)候會(huì)自動(dòng)將該模塊的導(dǎo)出符號(hào)表與系統(tǒng)自身的導(dǎo)出符號(hào)表合并。一個(gè)系統(tǒng)的源碼的導(dǎo)出符號(hào)表一般在源碼頂層目錄的modules.syms文件中,查看正在運(yùn)行的系統(tǒng)導(dǎo)出符號(hào)表使用cat /proc/kallsyms。注意,正如前面解釋的,我們的模塊之所以能夠正常運(yùn)行,一個(gè)重要原因就是編譯我們模塊使用的符號(hào)地址就是編譯內(nèi)核時(shí)使用的符號(hào)地址,所以運(yùn)行起來(lái)雖然地址會(huì)有偏移,但是模塊中相關(guān)的符號(hào)的地址也會(huì)和內(nèi)核地址一起偏移,也就還能找得到?;谶@種思想,我們也可以直接查看系統(tǒng)當(dāng)前運(yùn)行的地址,將地址賦值給一個(gè)函數(shù)指針并使用,也是沒(méi)有問(wèn)題的,當(dāng)然,這只是闡述原理,并不建議這么寫(xiě)模塊。
下面這個(gè)例子可以看出編譯出的地址和運(yùn)行時(shí)的地址是不一樣的:
iOS培訓(xùn),Swift培訓(xùn),蘋果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

導(dǎo)出符號(hào)表可以大大的提高系統(tǒng)的運(yùn)行效率,這也是只有開(kāi)源系統(tǒng)才能提供的一個(gè)強(qiáng)大的功能,但是,導(dǎo)出符號(hào)表的引入會(huì)導(dǎo)致一個(gè)小小的麻煩--模塊的依賴,當(dāng)我們使用lsmod的時(shí)候,就可以查看系統(tǒng)當(dāng)前的模塊,其最后兩列分別是該模塊被引用的次數(shù)以及引用該模塊的內(nèi)核模塊,當(dāng)一個(gè)模塊被其他模塊引用時(shí),我們是不能進(jìn)行卸載的,同樣,如果模塊A依賴于模塊B,那么如果模塊B不加載的時(shí)候模塊A也加載不了。在編寫(xiě)多模塊的時(shí)候尤其要注意這個(gè)問(wèn)題,可以寫(xiě)一個(gè)腳本管理多個(gè)依賴模塊。Linux內(nèi)核使用兩個(gè)宏來(lái)導(dǎo)出一個(gè)模塊的符號(hào)

EXPORT_SYMBOL(符號(hào)名)
EXPORT_SYMBOL_GPL(符號(hào)名)

模塊編譯方法

借助內(nèi)核的Makefile,編譯出的XXX.ko(Kernel Object)就是可加載到該內(nèi)核的外部模塊,為了利用內(nèi)核的Makefile,我們可以將編譯外部模塊的Makefile寫(xiě)成如下的格式:

ifneq ($(KERNELRELEASE),)    export-objs = demo.o
    obj-m = extern.oelse
  &