一個簡單的內核模塊實現
前言
這幾天因爲某些原因,需要學習下Linux內核模塊相關的知識,今天剛剛好學習完模塊的簡單實現,故將其整理出來,以供日後複習之用
一個簡單的內核模塊
模塊化的意義
在目前,內核的設計中,有兩種不同的趨勢,一種是單內核,另外一種是微內核,簡單而言
- 單內核就是一個很大的進程,在運行的時候,是一個單獨的二進制映像,模塊之間的通信是通過函數調用來實現
- 微內核則不同,各個模塊之間都作爲單獨的進程運行,模塊之間的通信是通過消息傳遞進行通信
關於這兩者的優勢,各有各的說法,本人研究也不深,無法做過多的點評,這裏我們只需要知道,Linux是單內核結構,但同時也支持一個模塊化的內核。
所謂的模塊化,就是各個部分以模塊的形式進行組織,可以根據需要對指定的模塊進行編譯,然後安裝到內核中即可,這種實現方式的優勢在於,不需要預先把一大堆的功能都編譯進內核,尤其是各種驅動,衆所周知,不同型號的硬件,對應的驅動不同,而如果爲了顧全所有的硬件,而把所有的驅動都編譯進內核,內核的體積會變得非常龐大,而且,當這些驅動需要進行更新的時候,必須要對內核重新進行編譯。
有了模塊化之後,可以根據需要將對應的模塊編譯進內核,並且可以動態的進行加載和卸載,這樣子,對應的模塊的維護以及系統的使用就簡單以及方便很多了
模塊的簡單實現
這裏我們來學習一個簡單模塊的實現
// hello_module.c
// 必備頭函數
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
// 該模塊的LICENSE
MODULE_LICENSE("GPL");
// 該模塊的作者
MODULE_AUTHOR("huanfeng.xu");
// 該模塊的說明
MODULE_DESCRIPTION("hello module");
// 初始化入口
// 模塊安裝時執行
// 這裏的__init 同樣是宏定義,主要的目的在於
// 高速內核,加載該模塊之後,可以回收init.text的區間
static int __init init_hello_module(void){
printk("KERN_INFO init the module. \n");
return 0;
}
// 模塊卸載時執行
// 同上
static void __exit exit_hello_module(void){
// 輸出信息,類似於printf()
// printk適用於內核模塊
printk("KERN_INFO exit the module. \n");
}
// 模塊初始化宏,用於加載該模塊
module_init(init_hello_module);
// 模塊卸載宏,用於卸載該模塊
module_exit(exit_hello_module);
需要注意的是,在普通的c開發中,每個程序都有一個main函數,作爲入口,而在內核中,則是module_init()
來負責
編寫對應的Makefile
# Makefile
# 內核源代碼所在位置
KERNEL_DIR = /lib/modules/`uname -r`/build
obj-m += hello_module.o
all:
make -C $(KERNEL_DIR) M=$(PWD) modules
clean:
rm *.o *.mod.c *.ko *.order *.symvers
模塊的編譯有兩種形式,一種是編譯成模塊,即上面的obj-m
,另一中是直接編譯到內核文件中,則上面的obj-m
需要更改爲obj-y
需要注意的是,由於我們是在內核源代碼之外編譯該模塊,所以在編譯的時候,需要暫時將編譯目錄切換到內核源代碼中,即上面的-C $(KERNEL_DIR)
,在Makefile中,可以聲明變量,即上面的KERNEL_DIR = /lib/modules/
uname -r/build
,使用時,直接$(KERNEL_DIR)
即可,這裏的$(PWD)
是內核自帶變量,所以無需聲明,可以直接使用
編寫完之後,直接執行make
即可,可以看到目錄下生成一個.ko文件,這就行對應的模塊了
模塊的安裝
由於該模塊比較簡單,也沒有依賴於其他模塊,所以安裝的時候可以直接使用insmod hello_module.ko
即可,安裝完成之後,可以使用dmesg查看是否有對應的內容輸出,如果操作沒有問題,則會看到這樣的日誌KERN_INFO init the module.
查看模塊是否已經安裝 lsmod | grep hello_module
查看模塊信息 modinfo hello_module
卸載模塊 rmmod hello_module
,卸載之後同樣可以使用dmesg看到
KERN_INFO exit the module.
帶參數的模塊
有時候我們需要在模塊安裝的時候,傳遞一些信息給模塊,可以使用如下方式
// 需要加上該頭文件
#include <linux/moduleparam.h>
module_param(name, type, perm);
// name爲安裝以及使用時的參數名字,type爲類型,pram爲對應的sysfs的權限
module_param_string(name, string, len, perm);
// name爲外部名字,string爲內部名字
module_param_array(name, type, nump, perm)
// nump用於存放數組項數
使用的方式爲,在安裝模塊的指定對應的參數及其值即可,如insmod hello_module size=100
導出符號表
由於模塊之間是相通的,所以模塊中定義的符號需要導出之後才能被其他模塊使用
導出方式只需要在使用MODULE_EXPORT(VAR_NAME)
或者MODULE_EXPORT_GPL(VAR_NAME)
即可,VAR_NAME可以爲變量,也可以爲函數
其他模塊只需要使用extern [TYPE] VAR_NAME
,即可在本模塊中使用其他模塊導出的變量或者函數了,需要注意的是,如果依賴其他模塊的導出符號,則在安裝該模塊的時候,對應的依賴模塊必須已經安裝好,否則會出現找不到對應符號的情況
總結
本小節主要簡單學習了模塊的寫法,模塊的編譯,傳遞參數以及導出符號,後面還會學習其他相關的內容,加油…