注:內容大多摘自《Linux設備驅動開發詳解》(第2版)
Linux內核模塊
1.1 簡介
1.特點:
- 模塊本身不被編譯入內核映像,從而控制了內核的大小;
- 模塊一旦被加載,它就和內核中的其他部分完全一樣。
2.模塊程序結構
(1)模塊加載函數(一般需要)
(2)模塊卸載函數(一般需要)
(3)模塊許可證聲明(必須)
(4)模塊參數(可選)
(5)模塊導出符號(可選)
(6)模塊作者等信息聲明(可選)
3.模塊的加載,卸載,查看:
- 加載:insmod命令
模塊加載後,在/sys/module/目錄下面將出現以此模塊名命名的目錄。 - 卸載:rmmod命令
- 查看:lsmod命令,查看所有加載的內核模塊
4.printk函數
內核模塊中用於輸出的函數是內核空間的printk()而非用戶空間的printf(),printk與printf用戶相似,但前者可定義輸出級別。printk可作爲一種最基本的內核調試手段。參考:printk 使用方法
備註:測試模塊例子時,終端沒能輸出printk的輸出信息,可以通過執行dmesg命令查看。參考:printk 無法輸出到控制檯
1.2,簡單模塊例子
1,模塊代碼(創建hello.c):
# include <linux/init.h>
# include <linux/module.h>
static int hello_init(void)
{
printk(KERN_EMERG "Hello World enter\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_EMERG "Hello World exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("Dual BSD/GPL");
2,編譯代碼 (創建Makefile文件):
## 執行uname -r 輸出爲 4.2.0-16-generic
KVERS = $(shell uname -r)
# Kernel modules
obj-m +=hello.o
#Specify flags for the module compliation
build: kernel_modules
kernel_modules:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
clean:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean
3,在模塊代碼和Makefile的當前目錄下執行make命令,生成hello.ko,調用insmod hello.ko掛載該模塊(可執行lsmod查看是否掛載成功),調用rmmod hello卸載該模塊。執行dmesg查看printk的輸出信息。
1.3 模塊聲明與描述
MOUDLE_AUTHOR(author); #作者
MOUDLE_DESCRIPTION(description); #描述
MOUDLE_VERSION(version); #版本
MODULE_DEVICE_TABLE(table_info); #設備表
MODULE_ALIAS(alternate_name); #別名
執行modeinfo <模塊名>可以獲取模塊信息,如modinfo hello.ko。
1.4 模塊參數
用module_param(參數名,參數類型,參數讀/寫權限)爲模塊定義一個參數,例子如下:
static char* book_name = "dissecinting Linux Device Driver";
static int num = 4000;
module_param(book_name, charp, S_IRUGO);
module_param(num, int, S_IRUGO);
# 裝載是執行命令:insmod param.ko book
參數類型可以是byte, short, ushort, int, uinit, long, ulong, charp(字符指針), bool或invbool(布爾的反),編譯時會將module_param中聲明的類型和變量定義的類型進行比較,判斷是否一致。
模塊加載後,在/sys/module/目錄下面將出現以此模塊名命名的目錄。當讀寫權限爲0時,表示此參數不存在sysfs文件系統下對應的文件節點;如果不爲0,在此模塊目錄下回出現parameter目錄,包含一系列以參數命名的文件節點。權限在include/linux/stat.h中有定義
使用 S_IRUGO 參數可以被所有人讀取, 但是不能改變; S_IRUGO|S_IWUSR 允許 root 來改變參數. 注意, 如果一個參數被 sysfs 修改, 你的模塊看到的參數值也改變了, 但是你的模塊沒有任何其他的通知. 你應當不要使模塊參數可寫, 除非你準備好檢測這個改變並且因而作出反應。
(參考:Linux之module_param()函數學習)
1.5 模塊的使用
嵌入式產品一般不需要建立模塊間依賴關係,所以modprobe可以不要,一般也不需要卸載模塊,所以rmmod也可以不要,但insmod是必須的(在Android設備中嘗試了insmod,rmmod,modinfo,lsmod等命令,都有存在,而modprobe不存在)。一般而言,產品在啓動過程中應該加載模塊,在嵌入式產品Linux的啓動過程中,最簡單的修改方法是修改啓動過程的rc腳本(可能是/init.rc文件),增加insmod /…/xxx.ko這樣的命令。用busybox做出的文件系統,通常修改etc/init.d/rcS文件。
1.6 導出符號
暫時不知道怎麼使用,類似外部函數
參考:導出符號的意義
1.7 附錄:
1,Hello World代碼:
(1)hello.c代碼
# include <linux/init.h>
# include <linux/module.h>
static int hello_init(void)
{
printk(KERN_EMERG "Hello World enter\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_EMERG "Hello World exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("Dual BSD/GPL");
(2)Makefile文件
## 執行uname -r 輸出爲 4.2.0-16-generic
KVERS = $(shell uname -r)
# Kernel modules
obj-m +=hello.o
#Specify flags for the module compliation
build: kernel_modules
kernel_modules:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
clean:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean
2,模塊參數例子代碼:
(1)文件:book.c
# include <linux/init.h>
# include <linux/module.h>
static char* book_name = "dissecinting Linux Device Driver";
static int num = 4000;
static int hello_init(void)
{
printk(KERN_EMERG "book name:%s\n", book_name);
printk(KERN_EMERG "book num:%d\n", num);
return 0;
}
static void hello_exit(void)
{
printk(KERN_EMERG "Hello World exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
module_param(book_name, charp, S_IRUGO);
module_param(num, int, S_IRUGO);
MODULE_LICENSE("Dual BSD/GPL");
(2)文件:Makefile
KVERS = $(shell uname -r)
# Kernel modules
obj-m +=book.o
#Specify flags for the module compliation
build: kernel_modules
kernel_modules:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
clean:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean