Linux内核开发:内核模块参数

本文详细介绍了如何在Linux内核模块中添加、读取和更改参数,包括使用module_param宏、验证参数值以及声明模块参数数组。通过示例展示了如何在加载模块时传递参数,并通过/sys接口在运行时修改参数。还提到了使用kernel_param_ops结构体来实现自定义的参数验证和读取功能。最后,讨论了module_param_array用于声明数组参数的情况。
摘要由CSDN通过智能技术生成

目录

使用参数加载模块

module_param宏

读取和更改参数值

验证参数值

声明模块参数数组

有关模块参数的一些说明


 

在本文中,我们将向模块添加参数。使用参数,您可以在加载模块时访问模块全局变量,并在运行时已加载模块时访问模块全局变量。

 

使用参数加载模块


使用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, &param_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

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值