核心模組學習之簡單的核心模組編寫
核心模組的特點
linux核心模組是一個編譯好的,具有特定格式的獨立目標檔案,使用者可通過系統提供的一組與模組相關的命令將核心模組載入進核心,當核心模組被載入後,它具有一下特點:
1.與核心一起執行在相同的核心態和核心地址空間
2.執行時和核心擁有一樣的特權級
3.可訪問核心中的各種資料結構
被載入到核心的核心模組程式碼和靜態編譯進核心的程式碼沒有區別,核心模組與核心中其他模組之間的互動只需要函式呼叫。當用戶不在需要某功能模組時,可將其從核心中解除安裝,配置靈活。
載入模組的管理
linux核心管理核心模組主要有兩項任務,核心符號表管理,維護核心模組引用計數。
1.核心將資源等級在符號表中,核心模組被載入後,模組可通過符號表使用核心中的資源,新模組載入到核心時,系統將新模組提供的符號加進
符號表中,這樣新載入模組就可訪問一家在模組提供的資源;在解除安裝一個模組時,系統釋放分配給該模組所用系統資源,同事將該模組提供的符號從符號
表中刪除。
2.引用計數是用來管理核心模組之間依賴性的。核心模組在載入後都在同一地址空間,核心模組之間可相互引用各自到處的符號,也就產生了依賴。如果A模組需要用到B模組到處的符號,而B模組沒有被載入到核心,A模組的載入就會出錯;同樣,一個核心模組如果有其他核心模組引用它匯出的符號,核心也不允許該模組被解除安裝。如果一個模組被依賴,它的引用技術就會增加,當依賴減少時,相應的引用計數也會減少,一個核心模組只有在引用計數為0的時候才能被解除安裝。
如何編寫一個核心模組
這裡介紹的核心模組程式設計以2.6核心版本為例,由於核心模組在核心空間執行,無法與c庫函式連結,因此它的編寫受到核心程式設計的限制,如不能使用浮點計算。
一個典型的核心模組結構應包含一下幾個部分:
1.標頭檔案宣告
#include<linux/init.h>
#include<linux/module.h>
#include<linux/moduleparam.h>
init.h中包含模組初始化和清理函式的定義;
module.h包含載入模組需要的函式和符號定義;
moduleparam.h允許模組載入時使用者傳遞引數。
2.模組許可宣告
MODULE_LICENSE("GPL");
模組需要通過MODULE_LICENSE巨集宣告此模組的許可證。從/usr/scr/linux_xxx/include/linux/module.h檔案可看到被核心接受的許可證
有GPL、GPL v2、GPL and additional rights、Dual BSD/GPL等
3.初始化和清理函式宣告
staticint xxx_init()
{
xxx;
return0;
}
staticvoid xxx_exit()
{
xxx;
}
module_init(xxx_init);
module_exit(xxx_exit);
核心模組必須使用巨集module_init和module_exit去註冊初始化與清理函式,而且初始化與清理函式必須在註冊之前定義。初始化與清理函式是配對使用的,例如module_init()中申請了一個資源,那麼module_exit()中就應該釋放這個資源。
總體來說我們需要做一下幾個工作:
1.檢視和下載當前版本的Linux原始碼
如果當前執行的核心版本與你編譯連結的標頭檔案版本不一致,載入核心模組時會遇到insmod: error inserting 'xxx.o' :-l invalid module format這樣的錯誤
可以使用uname -a檢視版本,在使用apt-get install linux-source 下載原始碼包。
#include<linux/init.h>
#include<linux/module.h>
#include<linux/moduleparam.h>
MODULE_LICENSE("GPL");
/* theinit function*/
inthello_init()
{
printk(" hello world !\n");
printk(" I have runing in akerner");
return 1;
}
/* thedistory function*/
inthello_exit()
{
printk("I will shut down myself inkernerl mod \n");
return 0;
}
module_init(hello_init);
module_exit(hello_exit);
2.編寫makefile
在與模組程式相同的目錄下編寫模組程式的Makefile檔案
obj-m := hello.o
KERNELDIR:= /lib/modules/$(shell uname -r)/build
PWD:= $(shell pwd)
modules:
$(MAKE)-C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE)-C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm-rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
說明:
1.執行該Makefile檔案時將跑到 KERNELDIR下呼叫make,即/lib/modules/$(shell uname -r) /build下必須有一個Makefiule檔案;
2.PWD指定要編譯的模組程式原始檔所在的目錄,$(shell pwd) 表示當前目錄,最終要生成的目標是modules
幾點說明
關於printfk
1.printk是核心提供的列印函式。printf是glibc提供的函式,linux核心函式是不能依賴於任何程式庫的;
2.printk()列印的資訊一般在/var/log/messages檔案裡;
3.可以使用dmesg命令檢視,如果只想顯示最後n行,可以用dmesg | tail -n。
關於模組的命令
1.載入
insmod/path/modulename 載入後在/sys/module中就可以看到模組了
2.解除安裝
rmmod/path/modulename 解除安裝後在/sys/module中模組移除
3.檢視系統以載入模組
lsmod
4.檢視模組資訊
modinfo/path/modulename