linux內核模塊

  • 一個簡單的驅動

  模塊的使用能使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

  1.  驅動初始化函數 XXX_init(void),使用宏module_init(test_init);告訴內核這是模塊的初始化函數入口。
  2.  驅動退出函數XXX_exot(void),使用宏module_exit(test_exit);告訴內核這是模塊的退出函數入口。
  3.    驅動模塊遵循的協議 MODULE_LICENSE("Dual BSD/GPL")

  注意到初始化函數和退出函數分別被__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才行。

相關文章
相關標籤/搜索