linux核心模組

來源:互聯網
上載者:User

標籤:

  • 一個簡單的驅動

  模組的使用能使linux核心便於裁剪,根據不同的應用需求得到一個最小的核心,同時調試核心驅動也更為方便,比如如果調試i2c驅動,如果不採用模組的方式,那麼每次修改i2c驅動就得編譯整個核心,對於編譯調試極其耗時,使用模組,一個簡單的insmod就將模組載入進了核心,如果覺得不合適,需要調試,只需要rmmod就可以將模組卸載。

  一個簡單的驅動模組:

 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);

   這個驅動什麼都沒幹,就列印了幾句話,但是具備了驅動程式最基本的幾個要素:

  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 notrace2 #define __exit          __section(.exit.text) __exitused __cold notrace

  是2個宏,其中__section也是個宏

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就是在卸載模組完成後,將它標識的函數所佔用的記憶體釋放。

  除了__init,__exit,還有__initdata,__exitdata。作用基本類似,我原本猜想帶data的應該是修飾僅在初始化函數和退出函數裡面用到的全域變數,比如一個數組之類的東西,但是我試了用__initdata修飾函數好像編譯並不報錯,暫時不深究。

  MODULE_LICENSE("Dual BSD/GPL"),少了它不影響編譯,但是載入模組的時候會警告提示這個模組汙染了核心:

  [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.symvers12 13 .PHONY:clean

 

 

 

  載入模組:  

1 sudo insmod test.ko

 

 

 

  為了圖方便,使用一個簡單的shell指令碼監視dmesg的輸出,指令碼如下:

 

1 #!/bin/bash2 3 set -o nounset                              # Treat unset variables as an error4 5 while true6 do7     dmesg -c 8     sleep 19 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 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
  和我在代碼中指定的一樣,原來許可權就是指這個檔案的許可權,參數與之對應的一個檔案,也就是說參數的預設值是可以被更改的,只要許可權拿到了,而我們使用insmod num=10的方法並不是修改這個預設值(很明顯,我指定num為10成功了,但是我並沒有對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才行。

linux核心模組

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.