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