1.6 內核模塊
1.6.1 內核模塊基礎知識
內核模塊是Linux內核向外部提供的一個插口,其全稱爲動態可加載內核模塊(Loadable Kernel Module,LKM),我們簡稱爲模塊。Linux內核之所以提供模塊機制,是因爲它本身是一個單內核(monolithic kernel)。單內核的最大優點是效率高,因爲所有的內容都集成在一起,但其缺點是可擴展性和可維護性相對較差,模塊機制就是爲了彌補這一缺陷。模塊是具有獨立功能的程序,它可以被單獨編譯,但不能獨立運行。它在運行時被鏈接到內核作爲內核的一部分在內核空間運行,這與運行在用戶空間的進程是不同的。模塊通常由一組函數和數據結構組成,用來實現一種文件系統、一個驅動程序或其他內核上層的功能。總之,模塊是一個爲內核(從某種意義上來說,內核也是一個模塊)或其他內核模塊提供使用功能的代碼塊。
1,內核模塊的特點
(1)模塊本身並不被編譯進內核文件(zImage或者bzImage)
(2)可以根據需求,在內核運行期間動態的安裝或者卸載。
2,安裝/卸載內核模塊
(1)安裝insmod
例:insmod dnw_usb.ko
(2)卸載rmmod
例:rmmod dnw_usb.ko
(3)查看 lsmod
例:lsmod
1.6.2 內核模塊的設計
1,內核模塊範例
#include<linux/init.h>
#include<linux/module.h>
static int hello_init(){
printk(KERN_WARNING"hello world!\n");
return 0;
}
static int hello_exit(){
printk(KERN_WARNING"hell exit!\n");
return 0;
}
module_init(hello_init);
module_exit(hello_exit);
內核通過printk()輸出的信息具有日誌級別,日誌級別是通過在printk()輸出的字符串前加一個帶尖括號的整數來控制的,如printk("<6>Hello, world!\n");。內核中共提供了八種不同的日誌級別,在 linux/kernel.h 中有相應的宏對應。
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
KERN_EMERG
用於突發性事件的消息,通常在系統崩潰之前報告此類消息。
KERN_ALERT
在需要立即操作的情況下使用此消息。
KERN_CRIT
用於臨界條件下,通常遇到嚴重的硬軟件錯誤時使用此消息。
KERN_ERR
用於報告錯誤條件;設備驅動經常使用KERN_ERR報告硬件難題。
KERN_WARNING
是關於問題狀況的警告,一般這些狀況不會引起系統的嚴重問題。
KERN_NOTICE
該級別較爲普通,但仍然值得注意。許多與安全性相關的情況會在這個級別被報告。
KERN_INFO
信息消息。許多驅動程序在啓動時刻用它來輸出獲得的硬件信息。
KERN_DEBUG
用於輸出調試信息。
2.編寫Makefile
obj-m:=helloworld.o
KDIR:=/home/win/Tiny6410/linux-tiny6410
all:
make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux-ARCH=arm
clean:
rm -f *.o,*.ko,*.order,*.symvers
obj-m := helloworld.o爲固定格式,指明內核模塊的名字,如該模塊有多個文件組成則須在下面加一行helloworld-objs := file1.o file2.o;KDIR := /home/win/Tiny6410/linux-tiny6410指明內核代碼位置;make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm,-C進入到內核代碼中,M指明內核模塊的位置。
1.6.3 內核模塊可選信息
1,模塊申明
(1)申明該模塊遵守的許可證協議,如:“GPL”,“GPL v2”等
MODULE_LICENSE("GPL")
(2)申明模塊的作者
MODULE_AUTHOR("JON")
(3)申明模塊的功能
MODULE_DESCRIPTION("描述模塊的功能")
(4)申明模塊的版本
MODULE_VERSION("V1.0")
2,模塊參數
在應用程序中int main(int argc, char** argv)。argc表示命令行輸入的參數個數,argv中保存輸入的參數。通過宏module_param指定保存模塊參數的變量。模塊參數用於在加載模塊時傳遞參數給模塊。
module_param(name,type,perm);
說明:
name:變量的名稱
type:變量類型,bool:布爾型 int:整型 charp:字符串型
perm:是訪問權限。S_IRUGO:讀權限 S_IWUSR:寫權限
3,符號導出
如果內核模塊中的變量或函數等要提供給其他內核模塊使用,必須使用宏EXPORT_SYMBOL(或者EXPORT_SYMBOL_GPL)將符號導出。
EXPORT_SYMBOL(符號名)
EXPORT_SYMBOL_GPL(符號名)
說明:其中EXPORT_SYMBOL_GPL只能用於包含GPL許可證的模塊。
//helloworld.c
#include<linux/init.h>
#include<linux/module.h>
extern int add(int a,int b);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("hello world");
MODULE_VERSION("V1.0");
int a=3;
module_param(a,int,S_IRUGO|S_IWUSR);
static int hello_init(){
printk(KERN_WARNING"hello world!\n");
printk("a=%d\n",a);
return 0;
}
static int hello_exit(){
int i;
i=add(3,4);
printk("3+4=%d\n");
printk(KERN_WARNING"hello exit!\n");
return 0;
}
module_init(hello_init);
module_exit(hello_exit);
//add.c
#include<linux/init.h>
#include<linux/module.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("hello world");
MODULE_VERSION("V1.0");
int add(int a,int b){
return (a+b);
}
static int add_init(){
return 0;
}
static int add_exit(){
return 0;
}
EXPORT_SYMBOL(add);
module_init(add_init);
module_exit(add_exit);