一、前言
Linux應用開發中,為使應用程序更加靈活地執行用戶的預期功能,我們有時候會通過命令行傳遞一些參數到main函數中,使得代碼邏輯可以依據參數執行不同的任務。同樣,Linux內核也提供了類似main函數傳參的內核傳參機制,編寫內核程序時只要實現傳參接口,用戶在加載內核模塊時即可傳入指定參數,使得內核模塊更加靈活。
二、內核模塊傳參
1、內核模塊傳參意義
內核模塊傳參會使得程序更加靈活,可以向上適配復雜的應用程序,向下兼容不同硬件設備;同時,通過參數選擇,可以避免重新編譯內核模塊,省時省力;另外,通過內核模塊傳遞參數也能更好地兼容和迭代產品。
2、內核傳參實現
內核支持傳遞的參數類型包含了C語言中常用的數據類型。
- 基本類型,字符型(char)、布爾型(bool)、整型(int)、長整型(long)、短整型(short),以及相關的無符號整型(unsigned)、字符指針(charp,內存為用戶提供的字符串分配,即char *)、顛倒了值的bool類型(invbool)
- 數組(array)
- 字符串(string)
實現內核模塊傳參,只需在內核模塊程序中調用module_param
系列宏即可,module_param
系列宏位于“/include/linux/moduleparam.h”
中定義,包括module_param_array
、module_param_string
、module_param_cb
。
#define module_param(name, type, perm) module_param_named(name, name, type, perm)
#define module_param_array(name, type, nump, perm) module_param_array_named(name, name, type, nump, perm)
#define module_param_string(name, string, len, perm) static const struct kparam_string __param_string_##name = { len, string }; __module_param_call(MODULE_PARAM_PREFIX, name, ¶m_ops_string, .str = &__param_string_##name, perm, -1, 0); __MODULE_PARM_TYPE(name, "string")
#define module_param_cb(name, ops, arg, perm) __module_param_call(MODULE_PARAM_PREFIX, name, ops, arg, perm, -1, 0)
module_param
用于處理基本類型參數,module_param_array
用于處理數組類型參數,module_param_string
用于處理字符串類型參數。
1)基本類型
module_param(name, type, perm)
name
,內核模塊程序中的變量名稱,同時又是用戶傳入參數時的名稱type
,參數類型,見上面perm
,該參數指定sysfs中相應文件的訪問權限,訪問權限與linux文件訪問權限相同的方式管理,位于"/include/linux/stat.h"
定義,一般使用S_IRUGO
;也可以直接用數字表示,如0444
表示S_IRUGO
/* include/linux/stat.h */
#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO) /* 所有用戶可讀、寫、執行 */
#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)/* 所有用戶可讀、寫、執行*/
#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH) /* 所有用戶可讀 */
#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH) /* 所有用戶可寫 */
#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH) /* 所有用戶可執行 */
/* include/uapi/linux/stat.h */
/* 三者分別表示用于者權限、用戶組權限、其他訪問者權限
* bit[0]、bit[1]、bit[2]分別表示可執行、可寫、可讀屬性
*/
#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100
#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
- S_I:只是一個前綴
- R:讀。W:寫。X:執行
- USR:用戶。GRP:組。UGO:用戶、組和其他
示例:
static int mode = 1;
static char *p = NULL;
module_param(mode, int , S_IRUGO); /* int型 */
module_param(p, charp, S_IRUGO); /* 指針 */
注:必須寫在模塊源文件的開頭部分(mode和*p是全局的);該宏不會聲明變量,因此在使用宏之前,必須聲明變量
2)數組類型
module_param_array(name, type, nump, perm)
name
,內核模塊程序中數組名稱,同時又是用戶傳入參數時的名稱type
,數組類型,int、char等nump
,指針,指向一個整數,其值表示有多少個參數存放在數組name中,用來接收用戶實際傳遞的數組成員的個數,內核接收到實際用戶傳遞的個數賦值到nump對應地址空間perm
,該參數指定sysfs訪問權限,位于"/include/linux/stat.h"
定義,一般使用S_IRUGO
;也可以直接用數字表示,如0444
表示S_IRUGO
示例:
static int array[3] = {0};
static int array_size;
module_param_array(array, int, &array_size, S_IRUGO);
3)字符串類型
module_param_string(name, string, len, perm)
name
,外部的參數名,可以與內核模塊程序中變量名稱string
相同string
,內部的變量名len
,以string命名的buffer大小(可以小于buffer的大小,但是沒有意義)perm
,該參數指定sysfs訪問權限(perm為零表示完全關閉相對應的sysfs項),位于"/include/linux/stat.h"
定義,一般使用S_IRUGO
;也可以直接用數字表示,如0444
表示S_IRUGO
示例:
static char string[6] = {0};
module_param_string(usestr, string, sizeof(string), S_IRUGO);
4)參數回調類型
module_param_cb(name, ops, arg, perm)
name
,內核模塊程序中的變量名稱,同時又是用戶傳入參數時的名稱ops
, 指針變量,指向被自定義的回調函數初始化的kernel_param_ops變量arg
, 指針變量,指向內核模塊程序中的變量名稱,保存用戶傳入的參數值perm
, 該參數指定sysfs訪問權限,位于"/include/linux/stat.h"
定義,一般使用S_IRUGO
;也可以直接用數字表示,如0444
表示S_IRUGO
這個宏用于在參數(參數)發生變化時注冊回調。例如,我使用 module_param 創建了一個參數debug,一旦我加載帶有 debug=0 的simple模塊,它將創建一個 sysfs 條目。并且我們想在不重新加載模塊的情況下打開調試消息,我們可以使用該文件:
[root@localhost tmp23]# ls -l /sys/module/simple/parameters/debug
-rw-r--r-- 1 root root 4096 May 23 14:46 /sys/module/simple/parameters/debug
當 sysfs 的值改變時(你可以使用 echo 1 > /sys/module/
改變),我們可以通過回調得到通知。如果您想在值發生變化時收到通知,我們需要將我們的處理函數注冊到它的文件操作結構中,即下面數據結構:
struct kernel_param_ops {
int (*set)(const char *val, const struct kernel_param *kp);
int (*get)(char *buffer, const struct kernel_param *kp);
void (*free)(void *arg);
}
注: module_param_array和module_param調用的是默認的回調函數, module_param_cb支持自定義回調函數
示例:
/*----------------------Module_param_cb()--------------------------------*/
static int debug= 0;
int custom_callback_function(const char *val, const struct kernel_param *kp)
{
int res = param_set_int(val, kp); // Use for write variable
if(res==0) {
printk(KERN_INFO "Call back function called...
");
printk(KERN_INFO "New value of debug = %d
", debug);
return 0;
}
return -1;
}
const struct kernel_param_ops my_param_ops =
{
.set = &custom_callback_function, // Use our setter ...
.get = ¶m_get_int, // .. and standard getter
};
module_param_cb(debug, &my_param_ops, &debug, S_IRUGO|S_IWUSR );
執行echo 1 > /sys/module/simple/parameters/debug
后,您可以看到調試變量發生了變化:
[root@localhost ~]# dmesg
[891496.255781] hello... debug=0
[891555.726272] Call back function called...
[891555.726277] New value of debug = 1
5)參數描述
用戶向內核模塊傳遞參數時,參數較多的情況下,開發工程師不易全部記住;因此,一般都會增加準確、清晰的參數描述信息,描述不同參數代表的含義,用戶調用時首先查詢驅動模塊的參數描敘信息,進而有目的地傳入具體參數。參數描述信息通過MODULE_PARM_DESC
宏實現,該宏位于“/include/linux/moduleparam.h”
中定義
#define MODULE_PARM_DESC(_parm, desc) __MODULE_INFO(parm, _parm, #_parm ":" desc)
_parm
,參數名稱desc
,描述信息,字符串類型
示例:
static int mode = 0;
module_param(mode, int , S_IRUGO);
MODULE_PARM_DESC(mode, "0:mode0; 1:mode1; 2:mode2");
module_param()
和module_param_array()
的作用就是讓那些全局變量對 insmod 可見,使模塊裝載時可重新賦值module_param_array()
宏的第三個參數用來記錄用戶 insmod 時提供的給這個數組的元素個數,NULL 表示不關心用戶提供的個數module_param()
和module_param_array()
最后一個參數權限值不能包含讓普通用戶也有寫權限,否則編譯報錯。這點可參考linux/moduleparam.h
中__module_param_call()
宏的定義- 字符串數組中的字符串似乎不能包含逗號或者空格,否則一個字符串會被解析成兩個或多個
3、內核模塊傳參實例
編寫一個基本的Linux內核模塊程序,實現命令行往內核模塊傳遞參數的功能,加載內核模塊時傳入指定參數。
內核模塊源碼如下:
#include < linux/module.h >
#include < linux/moduleparam.h >
#include < linux/init.h >
#include < linux/kernel.h >
/*傳遞整型類型數據*/
static int irq=10;
module_param(irq,int,0660);
MODULE_PARM_DESC(irq,"Interrupt range:1-32");
static int debug=0;
module_param(debug,int,0660);
MODULE_PARM_DESC(debug,"0:non debug mode; 1:debug mode");
/*傳遞指針類型數據*/
static char *devname = "simpdev";
module_param(devname,charp,0660);
MODULE_PARM_DESC(devname,"device name");
/*
傳遞數組類型數據
module_param_array(數組名, 元素類型, 元素個數(取地址), 權限);
*/
static int array[3];
static int count;
module_param_array(array, int, &count, 0660);
MODULE_PARM_DESC(array,"set array value");
/*
傳遞字符串: module_param_string
(傳遞參數時的字符串名稱, 字符串名稱, 字符串大小, 權限);
*/
static char string[20] = {0};
module_param_string(mystr, string, sizeof(string), 0660);
MODULE_PARM_DESC(mystr, "string variable of demonstration");
static int simple_init(void)
{
int i=0;
printk(KERN_WARNING "hello... irq=%d name=%s debug=%d
",irq,devname,debug);
for(i=0;i< 3; i++)
{
printk("array[%d]:%d
", i, array[i]);
}
printk("count=%d
",count);
printk("string=%s
", string);
return 0;
}
static void simple_cleanup(void)
{
printk(KERN_WARNING "bye... irq=%d name=%s debug=%d,count=%d, string=%s
",irq,devname,debug,count,string);
}
module_init(simple_init);
module_exit(simple_cleanup);
MODULE_LICENSE("GPL");