一、前言
Linux應(yīng)用開發(fā)中,為使應(yīng)用程序更加靈活地執(zhí)行用戶的預(yù)期功能,我們有時候會通過命令行傳遞一些參數(shù)到main函數(shù)中,使得代碼邏輯可以依據(jù)參數(shù)執(zhí)行不同的任務(wù)。同樣,Linux內(nèi)核也提供了類似main函數(shù)傳參的內(nèi)核傳參機制,編寫內(nèi)核程序時只要實現(xiàn)傳參接口,用戶在加載內(nèi)核模塊時即可傳入指定參數(shù),使得內(nèi)核模塊更加靈活。
二、內(nèi)核模塊傳參
1、內(nèi)核模塊傳參意義
內(nèi)核模塊傳參會使得程序更加靈活,可以向上適配復(fù)雜的應(yīng)用程序,向下兼容不同硬件設(shè)備;同時,通過參數(shù)選擇,可以避免重新編譯內(nèi)核模塊,省時省力;另外,通過內(nèi)核模塊傳遞參數(shù)也能更好地兼容和迭代產(chǎn)品。
2、內(nèi)核傳參實現(xiàn)
內(nèi)核支持傳遞的參數(shù)類型包含了C語言中常用的數(shù)據(jù)類型。
- 基本類型,字符型(char)、布爾型(bool)、整型(int)、長整型(long)、短整型(short),以及相關(guān)的無符號整型(unsigned)、字符指針(charp,內(nèi)存為用戶提供的字符串分配,即char *)、顛倒了值的bool類型(invbool)
- 數(shù)組(array)
- 字符串(string)
實現(xiàn)內(nèi)核模塊傳參,只需在內(nèi)核模塊程序中調(diào)用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
用于處理基本類型參數(shù),module_param_array
用于處理數(shù)組類型參數(shù),module_param_string
用于處理字符串類型參數(shù)。
1)基本類型
module_param(name, type, perm)
name
,內(nèi)核模塊程序中的變量名稱,同時又是用戶傳入?yún)?shù)時的名稱type
,參數(shù)類型,見上面perm
,該參數(shù)指定sysfs中相應(yīng)文件的訪問權(quán)限,訪問權(quán)限與linux文件訪問權(quán)限相同的方式管理,位于"/include/linux/stat.h"
定義,一般使用S_IRUGO
;也可以直接用數(shù)字表示,如0444
表示S_IRUGO
/* include/linux/stat.h */
#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO) /* 所有用戶可讀、寫、執(zhí)行 */
#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)/* 所有用戶可讀、寫、執(zhí)行*/
#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) /* 所有用戶可執(zhí)行 */
/* include/uapi/linux/stat.h */
/* 三者分別表示用于者權(quán)限、用戶組權(quán)限、其他訪問者權(quán)限
* bit[0]、bit[1]、bit[2]分別表示可執(zhí)行、可寫、可讀屬性
*/
#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:執(zhí)行
- 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)數(shù)組類型
module_param_array(name, type, nump, perm)
name
,內(nèi)核模塊程序中數(shù)組名稱,同時又是用戶傳入?yún)?shù)時的名稱type
,數(shù)組類型,int、char等nump
,指針,指向一個整數(shù),其值表示有多少個參數(shù)存放在數(shù)組name中,用來接收用戶實際傳遞的數(shù)組成員的個數(shù),內(nèi)核接收到實際用戶傳遞的個數(shù)賦值到nump對應(yīng)地址空間perm
,該參數(shù)指定sysfs訪問權(quán)限,位于"/include/linux/stat.h"
定義,一般使用S_IRUGO
;也可以直接用數(shù)字表示,如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
,外部的參數(shù)名,可以與內(nèi)核模塊程序中變量名稱string
相同string
,內(nèi)部的變量名len
,以string命名的buffer大?。梢孕∮赽uffer的大小,但是沒有意義)perm
,該參數(shù)指定sysfs訪問權(quán)限(perm為零表示完全關(guān)閉相對應(yīng)的sysfs項),位于"/include/linux/stat.h"
定義,一般使用S_IRUGO
;也可以直接用數(shù)字表示,如0444
表示S_IRUGO
示例:
static char string[6] = {0};
module_param_string(usestr, string, sizeof(string), S_IRUGO);
4)參數(shù)回調(diào)類型
module_param_cb(name, ops, arg, perm)
name
,內(nèi)核模塊程序中的變量名稱,同時又是用戶傳入?yún)?shù)時的名稱ops
, 指針變量,指向被自定義的回調(diào)函數(shù)初始化的kernel_param_ops變量arg
, 指針變量,指向內(nèi)核模塊程序中的變量名稱,保存用戶傳入的參數(shù)值perm
, 該參數(shù)指定sysfs訪問權(quán)限,位于"/include/linux/stat.h"
定義,一般使用S_IRUGO
;也可以直接用數(shù)字表示,如0444
表示S_IRUGO
這個宏用于在參數(shù)(參數(shù))發(fā)生變化時注冊回調(diào)。例如,我使用 module_param 創(chuàng)建了一個參數(shù)debug,一旦我加載帶有 debug=0 的simple模塊,它將創(chuàng)建一個 sysfs 條目。并且我們想在不重新加載模塊的情況下打開調(diào)試消息,我們可以使用該文件:
[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
當(dāng) sysfs 的值改變時(你可以使用 echo 1 > /sys/module/
改變),我們可以通過回調(diào)得到通知。如果您想在值發(fā)生變化時收到通知,我們需要將我們的處理函數(shù)注冊到它的文件操作結(jié)構(gòu)中,即下面數(shù)據(jù)結(jié)構(gòu):
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調(diào)用的是默認的回調(diào)函數(shù), module_param_cb支持自定義回調(diào)函數(shù)
示例:
/*----------------------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 );
執(zhí)行echo 1 > /sys/module/simple/parameters/debug
后,您可以看到調(diào)試變量發(fā)生了變化:
[root@localhost ~]# dmesg
[891496.255781] hello... debug=0
[891555.726272] Call back function called...
[891555.726277] New value of debug = 1
5)參數(shù)描述
用戶向內(nèi)核模塊傳遞參數(shù)時,參數(shù)較多的情況下,開發(fā)工程師不易全部記??;因此,一般都會增加準(zhǔn)確、清晰的參數(shù)描述信息,描述不同參數(shù)代表的含義,用戶調(diào)用時首先查詢驅(qū)動模塊的參數(shù)描敘信息,進而有目的地傳入具體參數(shù)。參數(shù)描述信息通過MODULE_PARM_DESC
宏實現(xiàn),該宏位于“/include/linux/moduleparam.h”
中定義
#define MODULE_PARM_DESC(_parm, desc) __MODULE_INFO(parm, _parm, #_parm ":" desc)
_parm
,參數(shù)名稱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()
宏的第三個參數(shù)用來記錄用戶 insmod 時提供的給這個數(shù)組的元素個數(shù),NULL 表示不關(guān)心用戶提供的個數(shù)module_param()
和module_param_array()
最后一個參數(shù)權(quán)限值不能包含讓普通用戶也有寫權(quán)限,否則編譯報錯。這點可參考linux/moduleparam.h
中__module_param_call()
宏的定義- 字符串?dāng)?shù)組中的字符串似乎不能包含逗號或者空格,否則一個字符串會被解析成兩個或多個
3、內(nèi)核模塊傳參實例
編寫一個基本的Linux內(nèi)核模塊程序,實現(xiàn)命令行往內(nèi)核模塊傳遞參數(shù)的功能,加載內(nèi)核模塊時傳入指定參數(shù)。
內(nèi)核模塊源碼如下:
#include < linux/module.h >
#include < linux/moduleparam.h >
#include < linux/init.h >
#include < linux/kernel.h >
/*傳遞整型類型數(shù)據(jù)*/
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");
/*傳遞指針類型數(shù)據(jù)*/
static char *devname = "simpdev";
module_param(devname,charp,0660);
MODULE_PARM_DESC(devname,"device name");
/*
傳遞數(shù)組類型數(shù)據(jù)
module_param_array(數(shù)組名, 元素類型, 元素個數(shù)(取地址), 權(quán)限);
*/
static int array[3];
static int count;
module_param_array(array, int, &count, 0660);
MODULE_PARM_DESC(array,"set array value");
/*
傳遞字符串: module_param_string
(傳遞參數(shù)時的字符串名稱, 字符串名稱, 字符串大小, 權(quán)限);
*/
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");