模塊編程屬于內(nèi)核編程,因此,除了對(duì)內(nèi)核相關(guān)知識(shí)有所了解外,還需要了解與模塊相關(guān)的知識(shí)。
1.應(yīng)用程序與內(nèi)核模塊的比較
為了加深對(duì)內(nèi)核模塊的了解,表一給出應(yīng)用程序與內(nèi)核模塊程序的比較。
表一 應(yīng)用程序與內(nèi)核模塊程序的比較
|
C語(yǔ)言應(yīng)用程序 |
內(nèi)核模塊程序 |
使用函數(shù) |
Libc庫(kù) |
內(nèi)核函數(shù) |
運(yùn)行空間 |
用戶空間 |
內(nèi)核空間 |
運(yùn)行權(quán)限 |
普通用戶 |
超級(jí)用戶 |
入口函數(shù) |
main() |
module_init() |
出口函數(shù) |
exit() |
module_exit() |
編譯 |
Gcc –c |
Makefile |
連接 |
Gcc |
insmod |
運(yùn)行 |
直接運(yùn)行 |
insmod |
調(diào)試 |
Gdb |
kdbug, kdb,kgdb等 |
從表一我們可以看出,內(nèi)核模塊程序不能調(diào)用libc庫(kù)中的函數(shù),它運(yùn)行在內(nèi)核空間,且只有超級(jí)用戶可以對(duì)其運(yùn)行。另外,模塊程序必須通過(guò)module_init()和module-exit()函數(shù)來(lái)告訴內(nèi)核“我來(lái)了”和“我走了”。
2.內(nèi)核符號(hào)表(如果對(duì)以下第2~4點(diǎn)理解上有困難,可以越過(guò))
如 前所述,Linux內(nèi)核是一個(gè)整體結(jié)構(gòu),像一個(gè)圓球,而模塊是插入到內(nèi)核中的插件。盡管內(nèi)核不是一個(gè)可安裝模塊,但為了方便起見(jiàn),Linux把內(nèi)核也看作 一個(gè)“母”模塊。那么模塊與模塊之間如何進(jìn)行交互呢,一種常用的方法就是共享變量和函數(shù)。但并不是模塊中的每個(gè)變量和函數(shù)都能被共享,內(nèi)核只把各個(gè)模塊中 主要的變量和函數(shù)放在一個(gè)特定的區(qū)段,這些變量和函數(shù)就統(tǒng)稱為符號(hào)。到低哪些符號(hào)可以被共享? Linux內(nèi)核有自己的規(guī)定。對(duì)于內(nèi)核這個(gè)特殊的母模塊,在kernel/ksyms.c中定義了從中可以“移出”的符號(hào),例如進(jìn)程管理子系統(tǒng)可以“移出”的符號(hào)定義如下:
/* 進(jìn)程管理 */
EXPORT_SYMBOL(do_mmap_pgoff);
EXPORT_SYMBOL(do_munmap);
EXPORT_SYMBOL(do_brk);
EXPORT_SYMBOL(exit_mm);
…
EXPORT_SYMBOL(schedule);
EXPORT_SYMBOL(jiffies);
EXPORT_SYMBOL(xtime);
…
你可能對(duì)這些變量和函數(shù)已經(jīng)很熟悉。其中宏定義EXPORT_SYMBOL()本身的含義是“移出符號(hào)”。為什么說(shuō)是“移出”呢?因?yàn)檫@些符號(hào)本來(lái)是內(nèi)核內(nèi)部的符號(hào),通過(guò)這個(gè)宏放在一個(gè)公開(kāi)的地方,使得裝入到內(nèi)核中的其他模塊可以引用它們。
實(shí)際上,僅僅知道這些符號(hào)的名字是不夠的,還得知道它們?cè)趦?nèi)核地址空間中的地址才有意義。因此,內(nèi)核中定義了如下結(jié)構(gòu)來(lái)描述模塊的符號(hào):
struct module_symbol
{
unsigned long value; /*符號(hào)在內(nèi)核地址空間中的地址*/
const char *name; /*符號(hào)名*/
};
我們可以從/proc/ksyms文件中讀取所有內(nèi)核模塊“移出”的符號(hào),這所有符號(hào)就形成內(nèi)核符號(hào)表,其格式如下:
內(nèi)存地址 符號(hào)名 [所屬模塊]
在模塊編程中,可以根據(jù)符號(hào)名從這個(gè)文件中檢索出其對(duì)應(yīng)的地址,然后直接訪問(wèn)該地址從而獲得內(nèi)核數(shù)據(jù)。第三列“所屬模塊”指符號(hào)所在的模塊名,對(duì)于從內(nèi)核這一母模塊移出的符號(hào),這一列為空。
模塊加載后,2.4內(nèi)核下可通過(guò) /proc/ksyms、 2.6 內(nèi)核下可通過(guò)/proc/kallsyms查看模塊輸出的內(nèi)核符號(hào)
3.模塊依賴
如前所述,內(nèi)核符號(hào)表記錄了所有模塊可以訪問(wèn)的符號(hào)及相應(yīng)的地址。當(dāng)一個(gè)新的模塊被裝入內(nèi)核后,它所申明的某些符號(hào)就會(huì)被登記到這個(gè)表中,而這些符號(hào)可能被其他模塊所引用,這就引出了模塊依賴這個(gè)問(wèn)題。
一個(gè)模塊A引用另一個(gè)模塊B所移出的符號(hào),我們就說(shuō)模塊B被模塊A引用,或者說(shuō)模塊A依賴模塊B。如果要鏈接模塊A,必須先鏈接模塊B。這種模塊間相互依賴的關(guān)系就叫模塊依賴。
4.模塊引用計(jì)數(shù)器
為 了確保模塊安全地卸載,每個(gè)模塊都有一個(gè)引用計(jì)數(shù)器。當(dāng)執(zhí)行模塊所涉及的操作時(shí)就遞增計(jì)數(shù)器,在操作結(jié)束時(shí)就遞減這個(gè)計(jì)數(shù)器;另外,當(dāng)模塊B被模塊A引用 時(shí),模塊B的引用計(jì)數(shù)就遞增,引用結(jié)束,計(jì)數(shù)器遞減。什么時(shí)候可以卸載這個(gè)模塊?當(dāng)然只有這個(gè)計(jì)數(shù)器值為0的時(shí)候,例如,當(dāng)一個(gè)文件系統(tǒng)還被安裝在系統(tǒng)上 時(shí)就不能將其卸載,當(dāng)這個(gè)文件系統(tǒng)不再被使用時(shí),引用計(jì)數(shù)器就為0,于是可以卸載。
四.模塊編譯
Linux 中最重要的軟件開(kāi)發(fā)工具是 GCC。GCC 是 GNU 的 C 和 C++ 編譯器。但是,在大型的開(kāi)發(fā)項(xiàng)目中,通常有幾十到上百個(gè)的源文件,如果每次均手工鍵入 gcc 命令進(jìn)行編譯的話,則會(huì)非常不方便。因此,人們通常利用 make 工具來(lái)自動(dòng)完成編譯工作。利用這種自動(dòng)編譯可大大簡(jiǎn)化開(kāi)發(fā)工作,避免不必要的重新編譯。這些工作包括:如果僅修改了某幾個(gè)源文件,則只重新編譯這幾個(gè)源文件;如果某個(gè)頭文件被修改了,則重新編譯所有包含該頭文件的源文件。
1.編譯工具make
實(shí)際上,make 工具通過(guò)一個(gè)稱為 Makefile 的文件來(lái)完成并自動(dòng)維護(hù)編譯工作。Makefile 需要按照某種語(yǔ)法進(jìn)行編寫,其中說(shuō)明了如何編譯各個(gè)源文件并連接生成可執(zhí)行文件,并定義了源文件之間的依賴關(guān)系。下面給出2.6 內(nèi)核模塊的Makefile模板(請(qǐng)參看Makefile的寫法)
# Makefile2.6 obj-m += hellomod.o # 產(chǎn)生hellomod 模塊的目標(biāo)文件 CURRENT_PATH := $(shell pwd) #模塊所在的當(dāng)前路徑 LINUX_KERNEL := $(shell uname -r) #Linux內(nèi)核源代碼的當(dāng)前版本 LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL) #Linux內(nèi)核源代碼的絕對(duì)路徑 all: make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules #編譯模塊了 clean: make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean #清理 |
注意: 在每個(gè)命令前(例如make命令前)要鍵入一個(gè)制表符(按TAB鍵產(chǎn)生)
有了Makefile,執(zhí)行make命令,會(huì)自動(dòng)形成相關(guān)的后綴為.o和.ko文件。
到此,模塊編譯好了,該把它插入到內(nèi)核了:
如:$insmod hellomod.ko
當(dāng)然,要以系統(tǒng)員的身份才能把模塊插入。
成功插入后,可以通過(guò)dmesg命令查看,屏幕最后幾行的輸出就是你程序中輸出的內(nèi)容:Hello,World! from the kernel space…
當(dāng)模塊不再需要時(shí),可以通過(guò)rmmod命令移去,例如
$rmmod hellomod