模塊的使用能使linux內核便於裁剪,根據不一樣的應用需求獲得一個最小的內核,同時調試內核驅動也更爲方便,好比若是調試i2c驅動,若是不採用模塊的方式,那麼每次修改i2c驅動就得編譯整個內核,對於編譯調試極其耗時,使用模塊,一個簡單的insmod就將模塊加載進了內核,若是以爲不合適,須要調試,只須要rmmod就能夠將模塊卸載。html
一個簡單的驅動模塊:linux
1 #include <linux/init.h> 2 #include <linux/module.h> 3 MODULE_LICENSE("Dual BSD/GPL"); 4 5 static int __init test_init(void) 6 { 7 printk(KERN_ALERT"test modules init\n"); 8 return 0; 9 } 10 11 static void __exit test_exit(void) 12 { 13 printk(KERN_ALERT"test modules exit\n"); 14 } 15 16 17 module_init(test_init); 18 module_exit(test_exit);
這個驅動什麼都沒幹,就打印了幾句話,可是具有了驅動程序最基本的幾個要素:shell
注意到初始化函數和退出函數分別被__init,__exit修限定,其實不用它們來限定2個函數也是能夠的,那麼用不用這2個東西限定有什麼區別呢?首先看它們究竟是2個什麼東西:數組
1 #define __init __section(.init.text) __cold notrace 2 #define __exit __section(.exit.text) __exitused __cold notrace
是2個宏,其中__section也是個宏bash
1 # define __section(S) __attribute__ ((__section__(#S)))
__attribute__是針對gcc的特有關鍵字,__attribute__ 能夠設置函數屬性(Function Attribute )、變量屬性(Variable Attribute )和類型屬性(Type Attribute ),我見的比較多的是在設置數據結構的對齊屬性上:數據結構
1 struct S { 2 3 short b[3]; 4 5 } __attribute__ ((aligned (8)));
在這裏設置爲.init.text是指把這個函數放到特殊的.init.text段,看過彙編代碼就可能有映象.text段,也就是代碼段,函數都是放這個段的。.init.text段的特性是等模塊加載完了,這個段將被釋放,由於就比如咱們寫一個單片機程序同樣,通常都會有個init函數,它完了就是個死循環,驅動實際在運行過程當中XXX_init函數就沒有什麼做用了,所以就能夠把XXX_init函數加載到內存的那段釋放掉,騰出空間來給別人用。函數
__exit就是在卸載模塊完成後,將它標識的函數所佔用的內存釋放。ui
除了__init,__exit,還有__initdata,__exitdata。做用基本相似,我本來猜測帶data的應該是修飾僅在初始化函數和退出函數裏面用到的全局變量,好比一個數組之類的東西,可是我試了用__initdata修飾函數好像編譯並不報錯,暫時不深究。this
MODULE_LICENSE("Dual BSD/GPL"),少了它不影響編譯,可是加載模塊的時候會警告提示這個模塊污染了內核:spa
[22321.826339] test: module license 'unspecified' taints kernel.
[22321.826349] Disabling lock debugging due to kernel taint
[22321.826759] test modules init
編譯模塊的makefile
1 ifneq ($(KERNELRELEASE), ) 2 obj-m := test.o book.o 3 else 4 KERNELDIR ?= /lib/modules/$(shell uname -r)/build 5 PWD := $(shell pwd) 6 defualt: 7 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 8 endif 9 10 clean: 11 rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules.order Module.symvers 12 13 .PHONY:clean
加載模塊:
1 sudo insmod test.ko
爲了圖方便,使用一個簡單的shell腳本監視dmesg的輸出,腳本以下:
1 #!/bin/bash 2 3 set -o nounset # Treat unset variables as an error 4 5 while true 6 do 7 dmesg -c 8 sleep 1 9 done
卸載模塊:
1 sudo rmmod test
卸載模塊不須要後面的.ko。
參數在驅動裏面必須是一個全局變量,通常加static限定。
聲明參數,使用宏:
1 module_param(<參數名>, <參數類型>, <參數讀寫權限>);
參數類型支持:byte、short、ushort、int、uint、long、ulong、charp(字符指針)、bool 或 invbool(布爾的反)
例如:
1 static int num = 5; 2 3 module_param(num, int, S_IRUGO); 4 5 static int __init test_init(void) 6 { 7 printk(KERN_ALERT"num:%d\n",num); 8 return 0; 9 }
若是在insmod的時候,指定num的值:
1 insmod test.ko num=10
那麼輸出:num:10
須要注意的是,參數在聲明時要給一個默認值,由於若是insmod指定參數值,那麼它就使用這個默認值。
一個參數(在驅動裏面的一個全局變量)怎麼會有讀寫權限?加載這個模塊後,進入/sys/module目錄,會看到一個test的目錄,這個目錄記錄了這個test模塊的一些信息,與未加模塊參數的時候相比,它多出了一個parameters的目錄,進如這個目錄,發現會有個與模塊參數同名的num文件,cat它裏面的內容就是它的默認值5,再查看這個num文件的權限:
-r--r--r-- 1 root root 4096 5月 3 21:30 num
和我在代碼中指定的同樣,原來權限就是指這個文件的權限,參數與之對應的一個文件,也就是說參數的默認值是能夠被更改的,只要權限拿到了。
使用 cat /proc/kallsyms | grep "\[test\]"查看與test.ko這個模塊相關的符號:
1 00000000 t test_exit [test] 2 00000000 r __param_num [test] 3 00000000 r __param_str_num [test] 4 00000000 d num [test] 5 00000000 d __this_module [test] 6 00000000 t cleanup_module [test]
能夠看到2個咱們預料中的符號,num和test_exit,若是咱們不使用__init限定test_init,那麼能夠看到test_init也在裏面,也就證實了__init的做用是在調用初始化函數後釋放了與之對應的內存:
1 00000000 t test_init [test] 2 00000000 t test_exit [test] 3 00000000 r __param_num [test] 4 00000000 r __param_str_num [test] 5 00000000 d num [test] 6 00000000 d __this_module [test] 7 00000000 t cleanup_module [test] 8 00000000 t init_module [test]
使用module_param是指外面能夠傳一個值進來,若是要這個值對外可見,咱們明顯不能採用去掉static限定的辦法,而是使用EXPORT_SYMBOL(num);這個宏是指將模塊內的全局變量num對外可見,它不單單能夠用於變量也能夠對函數起做用。
模塊A:
1 #include <linux/init.h> 2 #include <linux/module.h> 3 MODULE_LICENSE("Dual BSD/GPL"); 4 5 static int num = 5; 6 module_param(num, int, S_IRUGO); 7 8 static int __init test_init(void) 9 { 10 printk(KERN_ALERT"test modules init\n"); 11 return 0; 12 } 13 14 static void test_saymyname(void) 15 { 16 printk(KERN_ALERT"you call me\n"); 17 } 18 static void __exit test_exit(void) 19 { 20 printk(KERN_ALERT"test modules exit\n"); 21 } 22 23 24 module_init(test_init); 25 module_exit(test_exit); 26 EXPORT_SYMBOL(test_saymyname);
模塊B調用模塊A的 test_saymyname
1 #include <linux/init.h> 2 #include <linux/module.h> 3 MODULE_LICENSE("Dual BSD/GPL"); 4 5 extern void test_saymyname(void); 6 7 static int __init book_init(void) 8 { 9 test_saymyname(); 10 return 0; 11 } 12 13 static void __exit book_exit(void) 14 { 15 printk(KERN_ALERT"book modules exit\n"); 16 } 17 18 module_init(book_init); 19 module_exit(book_exit);
分別insmod test.ko和insmod book.ko,再卸載模塊時,若是先卸載test.ko會發現提示test.ko裏面有函數被book.ko調用,沒法卸載,只有先卸載book.ko才行。