一個(gè)簡(jiǎn)單的內(nèi)核模塊實(shí)現(xiàn)
前言
這幾天因?yàn)槟承┰颍枰獙W(xué)習(xí)下Linux內(nèi)核模塊相關(guān)的知識(shí)霸妹,今天剛剛好學(xué)習(xí)完模塊的簡(jiǎn)單實(shí)現(xiàn),故將其整理出來(lái),以供日后復(fù)習(xí)之用
一個(gè)簡(jiǎn)單的內(nèi)核模塊
模塊化的意義
在目前赊舶,內(nèi)核的設(shè)計(jì)中,有兩種不同的趨勢(shì)赶诊,一種是單內(nèi)核笼平,另外一種是微內(nèi)核,簡(jiǎn)單而言
- 單內(nèi)核就是一個(gè)很大的進(jìn)程舔痪,在運(yùn)行的時(shí)候寓调,是一個(gè)單獨(dú)的二進(jìn)制映像,模塊之間的通信是通過(guò)函數(shù)調(diào)用來(lái)實(shí)現(xiàn)
- 微內(nèi)核則不同锄码,各個(gè)模塊之間都作為單獨(dú)的進(jìn)程運(yùn)行夺英,模塊之間的通信是通過(guò)消息傳遞進(jìn)行通信
關(guān)于這兩者的優(yōu)勢(shì),各有各的說(shuō)法滋捶,本人研究也不深痛悯,無(wú)法做過(guò)多的點(diǎn)評(píng),這里我們只需要知道重窟,Linux是單內(nèi)核結(jié)構(gòu)载萌,但同時(shí)也支持一個(gè)模塊化的內(nèi)核。
所謂的模塊化,就是各個(gè)部分以模塊的形式進(jìn)行組織扭仁,可以根據(jù)需要對(duì)指定的模塊進(jìn)行編譯垮衷,然后安裝到內(nèi)核中即可,這種實(shí)現(xiàn)方式的優(yōu)勢(shì)在于乖坠,不需要預(yù)先把一大堆的功能都編譯進(jìn)內(nèi)核搀突,尤其是各種驅(qū)動(dòng),眾所周知瓤帚,不同型號(hào)的硬件描姚,對(duì)應(yīng)的驅(qū)動(dòng)不同,而如果為了顧全所有的硬件戈次,而把所有的驅(qū)動(dòng)都編譯進(jìn)內(nèi)核轩勘,內(nèi)核的體積會(huì)變得非常龐大,而且怯邪,當(dāng)這些驅(qū)動(dòng)需要進(jìn)行更新的時(shí)候绊寻,必須要對(duì)內(nèi)核重新進(jìn)行編譯。
有了模塊化之后悬秉,可以根據(jù)需要將對(duì)應(yīng)的模塊編譯進(jìn)內(nèi)核澄步,并且可以動(dòng)態(tài)的進(jìn)行加載和卸載,這樣子和泌,對(duì)應(yīng)的模塊的維護(hù)以及系統(tǒng)的使用就簡(jiǎn)單以及方便很多了
模塊的簡(jiǎn)單實(shí)現(xiàn)
這里我們來(lái)學(xué)習(xí)一個(gè)簡(jiǎn)單模塊的實(shí)現(xiàn)
// hello_module.c
// 必備頭函數(shù)
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
// 該模塊的LICENSE
MODULE_LICENSE("GPL")村缸;
// 該模塊的作者
MODULE_AUTHOR("huanfeng.xu");
// 該模塊的說(shuō)明
MODULE_DESCRIPTION("hello module");
// 初始化入口
// 模塊安裝時(shí)執(zhí)行
// 這里的__init 同樣是宏定義,主要的目的在于
// 高速內(nèi)核武氓,加載該模塊之后梯皿,可以回收init.text的區(qū)間
static int __init init_hello_module(void){
printk("KERN_INFO init the module. \n");
return 0;
}
// 模塊卸載時(shí)執(zhí)行
// 同上
static void __exit exit_hello_module(void){
// 輸出信息,類似于printf()
// printk適用于內(nèi)核模塊
printk("KERN_INFO exit the module. \n");
}
// 模塊初始化宏县恕,用于加載該模塊
module_init(init_hello_module);
// 模塊卸載宏东羹,用于卸載該模塊
module_exit(exit_hello_module);
需要注意的是,在普通的c開發(fā)中忠烛,每個(gè)程序都有一個(gè)main函數(shù)属提,作為入口,而在內(nèi)核中美尸,則是module_init()
來(lái)負(fù)責(zé)
編寫對(duì)應(yīng)的Makefile
# Makefile
# 內(nèi)核源代碼所在位置
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
师坎,另一中是直接編譯到內(nèi)核文件中恕酸,則上面的obj-m
需要更改為obj-y
需要注意的是,由于我們是在內(nèi)核源代碼之外編譯該模塊屹耐,所以在編譯的時(shí)候尸疆,需要暫時(shí)將編譯目錄切換到內(nèi)核源代碼中,即上面的-C $(KERNEL_DIR)
惶岭,在Makefile中寿弱,可以聲明變量,即上面的KERNEL_DIR = /lib/modules/
uname -r/build
按灶,使用時(shí)症革,直接$(KERNEL_DIR)
即可,這里的$(PWD)
是內(nèi)核自帶變量鸯旁,所以無(wú)需聲明噪矛,可以直接使用
編寫完之后,直接執(zhí)行make
即可铺罢,可以看到目錄下生成一個(gè).ko文件艇挨,這就行對(duì)應(yīng)的模塊了
模塊的安裝
由于該模塊比較簡(jiǎn)單,也沒(méi)有依賴于其他模塊韭赘,所以安裝的時(shí)候可以直接使用insmod hello_module.ko
即可缩滨,安裝完成之后,可以使用dmesg查看是否有對(duì)應(yīng)的內(nèi)容輸出泉瞻,如果操作沒(méi)有問(wèn)題脉漏,則會(huì)看到這樣的日志KERN_INFO init the module.
查看模塊是否已經(jīng)安裝 lsmod | grep hello_module
查看模塊信息 modinfo hello_module
卸載模塊 rmmod hello_module
,卸載之后同樣可以使用dmesg看到
KERN_INFO exit the module.
帶參數(shù)的模塊
有時(shí)候我們需要在模塊安裝的時(shí)候袖牙,傳遞一些信息給模塊侧巨,可以使用如下方式
// 需要加上該頭文件
#include <linux/moduleparam.h>
module_param(name, type, perm);
// name為安裝以及使用時(shí)的參數(shù)名字,type為類型鞭达,pram為對(duì)應(yīng)的sysfs的權(quán)限
module_param_string(name, string, len, perm);
// name為外部名字司忱,string為內(nèi)部名字
module_param_array(name, type, nump, perm)
// nump用于存放數(shù)組項(xiàng)數(shù)
使用的方式為,在安裝模塊的指定對(duì)應(yīng)的參數(shù)及其值即可碉怔,如insmod hello_module size=100
導(dǎo)出符號(hào)表
由于模塊之間是相通的烘贴,所以模塊中定義的符號(hào)需要導(dǎo)出之后才能被其他模塊使用
導(dǎo)出方式只需要在使用EXPORT_SYMBOL(VAR_NAME)
或者EXPORT_SYMBOL_GPL(VAR_NAME)
即可,VAR_NAME可以為變量撮胧,也可以為函數(shù)
其他模塊只需要使用extern [TYPE] VAR_NAME
桨踪,即可在本模塊中使用其他模塊導(dǎo)出的變量或者函數(shù)了,需要注意的是芹啥,如果依賴其他模塊的導(dǎo)出符號(hào)锻离,則在安裝該模塊的時(shí)候,對(duì)應(yīng)的依賴模塊必須已經(jīng)安裝好墓怀,否則會(huì)出現(xiàn)找不到對(duì)應(yīng)符號(hào)的情況
總結(jié)
本小節(jié)主要簡(jiǎn)單學(xué)習(xí)了模塊的寫法汽纠,模塊的編譯,傳遞參數(shù)以及導(dǎo)出符號(hào)傀履,后面還會(huì)學(xué)習(xí)其他相關(guān)的內(nèi)容虱朵,加油...