Linux設備驅動開發基礎---Linux內核模塊

一、Linux內核模塊的程序結構

模塊加載函數(必須) 

當通過insmod或 modprobe命令加載內核模塊時,模塊的加載函數會自動被內核執行,完成本模塊的相關初始化工作。 
模塊卸載函數(必須) 。 
當通過rmmod命令卸載某模塊時,模塊的卸載函數會自動被內核執行,完成與模塊加載函數相反的功能。 
模塊許可證聲明(必須) 。 
模塊許可證(LICENSE)聲明描述內核模塊的許可權限,如果不聲明 LICENSE,
模塊被加載時,將收到內核被污染 (kernel tainted)的警告。 
在 Linux  2.6 內核中,可接受的 LICENSE 包括“GPL” 、 “GPL  v2” 、 “GPL  and additional rights” 、 “Dual BSD/GPL” 、 “Dual MPL/GPL”和“Proprietary” 。 大多數情況下,內核模塊應遵循 GPL 兼容許可權。Linux  2.6 內核模塊最常見的是以 MODULE_LICENSE(  "Dual  BSD/GPL" )語句聲明模塊採用 BSD/GPL 雙LICENSE。 
模塊參數(可選) 。 
模塊參數是模塊被加載的時候可以被傳遞給它的值,它本身對應模塊內部的全局變量。 
模塊導出符號(可選) 。 
內核模塊可以導出符號(symbol,對應於函數或變量) ,這樣其他模塊可以使用本模塊中的變量或函數。 
模塊作者等信息聲明(可選) 。 

1、模塊加載函數

Linux內核模塊加載函數一般以_ _init標識聲明,典型的模塊加載函數的形式如下:

static int _ _init initialization_function(void) 
    {  
       /* 初始化代碼 */ 
    } 
module_init(initialization_function); 

模塊加載函數必須以“module_init(函數名)”的形式被指定。它返回整型值,若初始化成功,應返回0。而在初始化失敗時,應該返回錯誤編碼。

在Linux 2.6 內核中,可以使用request_module(const  char *fmt, …)函數加載內核
模塊,驅動開發人員可以通過調用 

request_module(module_name);  
或 
request_module("char-major-%d-%d", MAJOR(dev), MINOR(dev)); 

來加載其他內核模塊。 

在Linux 內核中, 所有標識爲_ _init的函數在連接的時候都放在.init.text這個區段內,此外,所有的_  _init函數在區段.initcall.init中還保存了一份函數指針,在初始化
時內核會通過這些函數指針調用這些_ _init函數, 並在初始化完成後釋放 init區段(包括.init.text,.initcall.init等)。 

2、模塊卸載函數

Linux內核模塊卸載函數一般以_ _exit標識聲明, 典型的模塊卸載函數的形式如下:

static void _ _exit cleanup_function(void) 
  { 
           /* 釋放代碼 */ 
   } 
module_exit(cleanup_function); 

模塊卸載函數在模塊卸載的時候執行,不返回任何值,必須以“module_exit(函數名)”的形式來指定。

 和_ _init一樣,_ _exit也可以使對應函數在運行完成後自動回收內存。實際上,_ _init和_ _exit都是宏,數據也可以被定義爲_initdata 和_exitdata。 

3、模塊參數

我們可以用“module_param(參數名,參數類型,參數讀/寫權限)”爲模塊定義一個參數,例如下列代碼定義了一個整型參數和一個字符指針參數: 

static char *book_name = "Linux Device Drivers, Third Edition"; 
static int num = 4000; 
module_param(num, int, S_IRUGO); 
module_param(book_name, charp, S_IRUGO); 

在裝載內核模塊時,用戶可以向模塊傳遞參數,形式爲“insmode(或 modprobe)
模塊名  參數名=參數值” ,如果不傳遞,參數將使用模塊內定義的默認值。 
參數類型可以是byte、short、ushort、int、uint、long、ulong、charp(字符指針) 、bool 或 invbool(布爾的反) ,在模塊被編譯時會將 module_param 中聲明的類型與變量定義的類型進行比較,判斷是否一致。 

除此之外,模塊也可以擁有參數數組,形式爲“module_param_array(數組名,數組類型,數組長,參數讀/寫權限)” 。

4、導出符號

Linux 2.6的“/proc/kallsyms”文件對應着內核符號表,它記錄了符號以及符號所在的內存地址。 
模塊可以使用如下宏導出符號到內核符號表: 
EXPORT_SYMBOL(符號名); 
EXPORT_SYMBOL_GPL(符號名); 
導 出的符號將可以被其他模塊使用 , 使用前聲明一下即可 。

5、模塊聲明與描述

在Linux內核模塊中, 我們可以用MODULE_AUTHOR、 MODULE_DESCRIPTION、MODULE_ VERSION、MODULE_DEVICE_TABLE、MODULE_ALIAS分別聲明模塊的
作者、描述、版本、設備表和別名,例如: 

MODULE_AUTHOR(author); 
MODULE_DESCRIPTION(description); 
MODULE_VERSION(version_string); 
MODULE_DEVICE_TABLE(table_info); 
MODULE_ALIAS(alternate_name); 

對於USB、PCI等設備驅動,通常會創建一個 MODULE_DEVICE_TABLE。

6、模塊的編譯 

我們以hello.c爲例的模板編寫一個簡單的 Makefile,如下所示: 
obj-m := hello.o 
並使用如下命令編譯Hello World模塊,如下所示: 
make -C /usr/src/linux-2.6.15.5/ M=/driver_study/ modules 
如果當前處於模塊所在的目錄,則以下命令與上述命令同等: 
make –C /usr/src/linux-2.6.15.5 M=$(pwd) modules 
其中-C後指定的是Linux內核源代碼的目錄, 而M=後指定的是hello.c和Makefile所在的目錄。 

如果一個模塊包括多個.c 文件(如 file1.c、file2.c) ,則應該以如下方式編寫
Makefile: 
obj-m := modulename.o 
module-objs := file1.o file2.o  

7、模塊與 GPL

 對於自己編寫的驅動等內核代碼,如果不編譯爲模塊則無法繞開GPL,編譯爲模塊後企業在產品中使用模塊,則公司對外不再需要提供對應的源代碼,爲了使公司產品所使用的Linux 操作系統支持模塊,需要完成如下工作。 
在內核編譯時應該選上“Enable  loadable module  support” ,嵌入式產品一般不需要動態卸載模塊,所以“可以卸載模塊”不用選,當然選了也沒關係。如果有項目被選擇“M” ,則編譯時除了make bzImage以外,也要 make modules。  

將我們編譯的內核模塊.ko文件放置在目標文件系統的相關目錄中。 

在使用中用戶可使用 insmod命令手動加載模塊,如 insmod xxx.ko。 但是一般而言,產品在啓動過程中應該加載模塊,在嵌入式 Linux的啓動過程中,加載企業自己的模塊的最簡單的方法是修改啓動過程的rc腳本,增加insmod  /.../xxx.ko 這樣的命令。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章