模块也可以从命令行获取参数。但不是通过以前你习惯的argc/argv。
要传递参数给模块,首先将获取参数值的变量声明为全局变量。然后使用宏MODULE_PARM()
(在头文件linux/module.h)。运行时,insmod将给变量赋予命令行的参数,如同 ./insmod mymodule.o myvariable=5。为使代码清晰,变量的声明和宏都应该放在 模块代码的开始部分。以下的代码范例也许将比我公认差劲的解说更好。
宏MODULE_PARM()
需要两个参数,变量的名字和其类型。支持的类型有" b": 比特型,"h": 短整型, "i": 整数型," l: 长整型和 "s": 字符串型,其中正数型既可为signed也可为unsigned。 字符串类型应该声明为"char *"这样insmod就可以为它们分配内存空间。你应该总是为你的变量赋初值。 这是内核编程,代码要编写的十分谨慎。举个例子:
int myint = 3; char *mystr; MODULE_PARM(myint, "i"); MODULE_PARM(mystr, "s");
数组同样被支持。在宏MODULE_PARM中在类型符号前面的整型值意味着一个指定了最大长度的数组。 用'-'隔开的两个数字则分别意味着最小和最大长度。下面的例子中,就声明了一个最小长度为2,最大长度为4的整形数组。
int myshortArray[4]; MODULE_PARM (myintArray, "3-9i");
将初始值设为缺省使用的IO端口或IO内存是一个不错的作法。如果这些变量有缺省值,则可以进行自动设备检测, 否则保持当前设置的值。我们将在后续章节解释清楚相关内容。在这里我只是演示如何向一个模块传递参数。
最后,还有这样一个宏,MODULE_PARM_DESC()
被用来注解该模块可以接收的参数。该宏 两个参数:变量名和一个格式自由的对该变量的描述。
Example 2-7. hello-5.c
/* * hello-5.c - Demonstrates command line argument passing to a module. */ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/stat.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Peter Jay Salzman"); static short int myshort = 1; static int myint = 420; static long int mylong = 9999; static char *mystring = "blah"; /* * module_param(foo, int, 0000) * The first param is the parameters name * The second param is it's data type * The final argument is the permissions bits, * for exposing parameters in sysfs (if non-zero) at a later stage. */ module_param(myshort, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); MODULE_PARM_DESC(myshort, "A short integer"); module_param(myint, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); MODULE_PARM_DESC(myint, "An integer"); module_param(mylong, long, S_IRUSR); MODULE_PARM_DESC(mylong, "A long integer"); module_param(mystring, charp, 0000); MODULE_PARM_DESC(mystring, "A character string"); static int __init hello_5_init(void) { printk(KERN_ALERT "Hello, world 5/n=============/n"); printk(KERN_ALERT "myshort is a short integer: %hd/n", myshort); printk(KERN_ALERT "myint is an integer: %d/n", myint); printk(KERN_ALERT "mylong is a long integer: %ld/n", mylong); printk(KERN_ALERT "mystring is a string: %s/n", mystring); return 0; } static void __exit hello_5_exit(void) { printk(KERN_ALERT "Goodbye, world 5/n"); } module_init(hello_5_init); module_exit(hello_5_exit);
我建议用下面的方法实验你的模块:
satan# insmod hello-5.o mystring="bebop" mybyte=255 myintArray=-1 mybyte is an 8 bit integer: 255 myshort is a short integer: 1 myint is an integer: 20 mylong is a long integer: 9999 mystring is a string: bebop myintArray is -1 and 420 satan# rmmod hello-5 Goodbye, world 5 satan# insmod hello-5.o mystring="supercalifragilisticexpialidocious" / > mybyte=256 myintArray=-1,-1 mybyte is an 8 bit integer: 0 myshort is a short integer: 1 myint is an integer: 20 mylong is a long integer: 9999 mystring is a string: supercalifragilisticexpialidocious myintArray is -1 and -1 satan# rmmod hello-5 Goodbye, world 5 satan# insmod hello-5.o mylong=hello hello-5.o: invalid argument syntax for mylong: 'h' module_param(name, type, perm)是一个宏,向当前模块传入参数,对源码分析如下 在include/linux/moduleparam.h中 #define module_param(name, type, perm) / module_param_named(name, name, type, perm) #define module_param_named(name, value, type, perm) / param_check_##type(name, &(value)); / module_param_call(name, param_set_##type, param_get_##type, &value, perm); / __MODULE_PARM_TYPE(name, #type) #define module_param_call(name, set, get, arg, perm) / __module_param_call(MODULE_PARAM_PREFIX, name, set, get, arg, perm) #define __module_param_call(prefix, name, set, get, arg, perm) / /* Default value instead of permissions? */ / static int __param_perm_check_##name __attribute__((unused)) = / BUILD_BUG_ON_ZERO((perm) < 0 || (perm) > 0777 || ((perm) & 2)); / static char __param_str_##name[] = prefix #name; / static struct kernel_param const __param_##name / __attribute_used__ / __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) / = { __param_str_##name, perm, set, get, arg } __attibute__ 是gcc的关键字,可参考 http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Variable-Attributes.html __attibute__将参数编译到__param段中, module_param是一步一步展开的, 上面几个宏在include/linux/moduleparam.h中的顺序刚好相反 module_param宏的类似函数调用的顺序 module_param->module_param_named->module_param_call->__module_param_call 展开的顺序正好相反 __module_param_call->module_param_call->module_param_named->module_param type类型可以是byte,short,ushort,int, uint,long,ulong,charp(注:字符指针),bool,invbool, perm表示此参数在sysfs文件系统中所对应的文件节点的属性。 权限在include/linux/stat.h中有定义 比如: #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 当perm为0时,表示此参数不存在 sysfs文件系统下对应的文件节点。 模块被加载后,在/sys/module/ 目录下将出现以此模块名命名的目录。 测试一下,插入一个驱动模块mp.ko, sudo insmod mp.ko 发现 /sys/module下出现mp这个文件夹 mp中有drivers/,sections/,parameters/,initstate,refcnt,srcversion 其中initstate 里面内容为:live refcnt里面内容为:0 srcversion里面内容为:F9BDEBF706329B443C28E08,很长,应该是一个唯一数字 driver文件夹里面是空的 sections有__param,__versions __param里面内容为0xd081e0a8 __versions里面内容为0xd081e100 parameters有count,info(注:自己定义的参数) count里面的内容为1(注:输出次数) info里面的内容为a site about linux driver.(注:输出内容) 如果此模块存在perm不为0的命令行参数, 在此模块的目录下将出现parameters目录, 包含一系列以参数名命名的文件节点, 这些文件的权限值等于perm,文件的内容为参数的值。 在/sys/module/mp/出现的参数,在module结构中的对应如下 struct module { enum module_state state; /* Member of list of modules */ struct list_head list; ...... /* Sysfs stuff. */ struct module_kobject mkobj; struct module_param_attrs *param_attrs; struct module_attribute *modinfo_attrs; const char *version; const char *srcversion; struct kobject *drivers_dir; /* Exported symbols */ const struct kernel_symbol *syms; unsigned int num_syms; const unsigned long *crcs; ...... /* Section attributes */ struct module_sect_attrs *sect_attrs; #endif ...... }; enum module_state { MODULE_STATE_LIVE, MODULE_STATE_COMING, MODULE_STATE_GOING, }; state对应/sys/module/mp/initstate drivers_dir对应/sys/module/mp/driver文件夹 version对应/sys/module/mp/sections/__version srcversion对应/sys/module/mp/srcversion 测试模块,源程序mp.c内容如下:编译手工插入,插入时传递参数 count=10,info="驱动开发网站。"命令如下:sudo insmod mp.ko count=10 info="驱动开发网站。"然后用dmesg查看消息看到如下,共10条记录[16122.280000]www.linuxdriver.cn is 驱动开发网站[16122.280000]www.linuxdriver.cn is 驱动开发网站[16122.280000]www.linuxdriver.cn is 驱动开发网站[16122.280000]www.linuxdriver.cn is 驱动开发网站[16122.280000]www.linuxdriver.cn is 驱动开发网站[16122.280000]www.linuxdriver.cn is 驱动开发网站[16122.280000]www.linuxdriver.cn is 驱动开发网站[16122.280000]www.linuxdriver.cn is 驱动开发网站[16122.280000]www.linuxdriver.cn is 驱动开发网站[16122.280000]www.linuxdriver.cn is 驱动开发网站#include <linux/module.h> #include <linux/moduleparam.h> static char *info= "a site about linux driver."; static int count= 1; static int mp= 0; module_param(mp,int,0); module_param(count,int,S_IRUSR); module_param(info,charp,S_IRUSR); MODULE_AUTHOR("ioctrl"); MODULE_LICENSE("Dual BSD/GPL"); static int hello_init(void) { int i; for(i=0;i<count;i++) { printk(KERN_NOTICEwww.linuxdriver.cn is "); printk(info); printk("/n"); } return 0; } static void hello_exit(void) { printk(KERN_NOTICE"exit!/n"); } module_init(hello_init); module_exit(hello_exit);