目录
在本文中,我们将向模块添加参数。使用参数,您可以在加载模块时访问模块全局变量,并在运行时已加载模块时访问模块全局变量。
使用参数加载模块
使用insmod命令加载模块时,可以提供参数作为key = value对,例如:
# insmod ./mymod.ko irq=20 name=mydev debug=1 address=0x1000,0x2000,0x3000
参数可以是数字,字符串或(数字或字符串的)数组
要在模块中声明一个简单参数,请声明一个全局变量并使用module_param宏,例如:
#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);
static int debug=0;
module_param(debug,int,0660);
static char *devname = "simpdev";
module_param(devname,charp,0660);
static int simple_init(void)
{
printk(KERN_WARNING "hello... irq=%d name=%s debug=%d\n",irq,devname,debug);
return 0;
}
static void simple_cleanup(void)
{
printk(KERN_WARNING "bye... irq=%d name=%s debug=%d\n",irq,devname,debug);
}
module_init(simple_init);
module_exit(simple_cleanup);
现在,您可以使用irq,debug和devname参数编译和加载模块:
# insmod ./simple.ko irq=44 devname=simpdev debug=0
全局变量将在使用use dmesg的init函数查看输出之前进行初始化:
# dmesg
...
[ 144.526050] hello... irq=44 name=simpdev debug=0
module_param宏
宏原型是
module_param(name, type, perm);
名称–已经定义的变量的名称
type –参数类型(int,long,charp等)
权限– / sys / module / <模块名称> / parameters / <param>的权限(对于没有这样的模块参数值文件,则为0)
读取和更改参数值
/ sys / module / <模块名称> / parameters中的文件用于在加载模块时读取/写入参数值。例如,如果我们加载了一个带有debug = 0的模块,并且想要在不重新加载模块的情况下打开调试消息,则可以使用以下文件:
# echo "1">/sys/module/simple/parameters/debug
您可以在卸载模块时看到调试变量已更改
# rmmod simple
# dmesg
...
[ 362.125050] bye... irq=44 name=simpdev debug=0
验证参数值
使用/ sys中的文件,如果有权限,我们可以设置所需的任何值。例如,如果irq只能在1到32的范围内,则无法验证它,因为用户可以直接访问该变量。为了解决这个问题,我们可以实现一个用于设置获取参数值的接口– kernel_param_ops
struct kernel_param_ops {
/* How the ops should behave */
unsigned int flags;
/* Returns 0, or -errno. arg is in kp->arg. */
int (*set)(const char *val, const struct kernel_param *kp);
/* Returns length written or -errno. Buffer is 4k (ie. be short!) */
int (*get)(char *buffer, const struct kernel_param *kp);
/* Optional function to free kp->arg when module unloaded. */
void (*free)(void *arg);
};
要实现接口,我们需要定义一个变量,实现set / get方法并使用module_param_cb注册它:
static int my_set(const char *val, const struct kernel_param *kp)
{
int n = 0, ret;
ret = kstrtoint(val, 10, &n);
if (ret != 0 || n < 1 || n > 32)
return -EINVAL;
return param_set_int(val, kp);
}
static const struct kernel_param_ops param_ops = {
.set = my_set,
.get = param_get_int,
};
static int num;
module_param_cb(simpcb, ¶m_ops, &num, 0664);
实际的参数值保存在num中,set函数将用户输入作为字符串并将其转换为int,然后我们检查该值是否正确(1-32)并使用内核提供的param_set_int函数进行设置。要返回参数值,我们使用提供的param_get_int函数
如果尝试使用错误的值加载模块,或者尝试使用/ sys文件设置错误的值,则会出现错误:
$ sudo insmod ./simp.ko irq=9 devname=mydev debug=0 simpcb=200
insmod: ERROR: could not insert module ./simp.ko: Invalid parameters
# echo "70">/sys/module/simp/parameters/simpcb
bash: echo: write error: Invalid argument
我们还可以为get定义一个自定义函数,但在此示例中它不是必需的。如果希望使用字符串提供参数值但另存为数字,这将很有用
例如,如果我们希望中断模式的参数为水平,边沿,轮询中的一个,但我们想将其保存为数字(1 –水平,2边沿,3轮询),则将定义set函数以从将字符串转换为int并将get函数从int转换为字符串
例如:
enum irq_type {
IRQ_TYPE_LEVEL,
IRQ_TYPE_EDGE,
IRQ_TYPE_POLLING
};
static int irq_type = IRQ_TYPE_LEVEL; // default
static int irqtype_op_write_handler(const char *val, const struct kernel_param *kp)
{
char valcp[16];
char *s;
strncpy(valcp, val, 16);
valcp[15] = '\0';
s = strstrip(valcp);
if (strcmp(s, "level") == 0)
irq_type = IRQ_TYPE_LEVEL;
else if (strcmp(s, "edge") == 0)
irq_type = IRQ_TYPE_EDGE;
else if (strcmp(s, "polling") == 0)
irq_type = IRQ_TYPE_POLLING;
else
return -EINVAL;
return 0;
}
static int irqtype_op_read_handler(char *buffer, const struct kernel_param *kp)
{
switch (irq_type) {
case IRQ_TYPE_LEVEL:
strcpy(buffer, "Level");
break;
case IRQ_TYPE_EDGE:
strcpy(buffer, "Edge");
break;
case IRQ_TYPE_POLLING:
strcpy(buffer, "Polling");
break;
default:
strcpy(buffer, "error");
break;
}
return strlen(buffer);
}
static const struct kernel_param_ops irqtype_op_ops = {
.set = irqtype_op_write_handler,
.get = irqtype_op_read_handler
};
module_param_cb(irqtype, &irqtype_op_ops, NULL, 0660);
要加载内核模块或更改参数值,请使用字符串:
# sudo insmod ./simp.ko irq=9 devname=mydev debug=0 simpcb=20 irqtype=edge
# cat /sys/module/simp/parameters/irqtype
Edge
声明模块参数数组
您可以使用module_param_array将参数声明为数字或字符串数组。例如:
static int addr[SIZE];
static int count;
module_param_array(addr, int, &count, 0660);
加载模块时,请使用逗号分隔的列表来提供值:
# insmod ./simp.ko addr=0x1000,0x2000,0x3000
count是一个输出参数,并使用用户提供的元素数进行更新(在此示例中为3)
有关模块参数的一些说明
- 如果使用modprobe加载模块,则可以在/etc/modprobe.conf(或其他文件-取决于您的发行版)中设置参数。
- 如果使用内核(静态)编译模块,则可以在启动时使用内核命令行提供参数。
- 还有更多功能–查看源代码(moduleparam.h)
源代码:https://github.com/dev-area/module-params
或者:https://github.com/Rtoax/test/tree/master/linux/module/module-params