在Linux内核驱动中,我们可能需要传递一些参数来配置我们的驱动程序。这些参数可以是用户可见的,如模块的名称、地址和大小,也可以是不可见的,如调试标志、日志等。为了达到这个目标, 我们可以使用 module_param 来定义和处理内核模块的参数。
module_param 是内核模块编程中的一个非常重要的概念,它允许我们向模块传递参数,使模块在不同的系统上能够更加灵活运行。在这篇文章中,我们将深入探讨如何使用 module_param 在 Linux 内核驱动中传递参数。
什么是module_param?
module_param 是一个宏,用于定义和处理内核模块的参数。它提供了一种简单的方法来实现内核模块参数的命令行控制,这些参数可以在加载时或运行时进行修改。
module_param 具有三个参数:名称、类型和访问权限,如下所示:
```c
module_param(name, type, permission);
```
其中,name 是参数的名称,type 是参数的类型,permission 是参数的访问权限。下面是一个例子:
```c
module_param(my_value, int, S_IRUSR | S_IWUSR);
```
上面的这个例子定义了一个叫做 my_value 的整数类型参数,并且允许用户读写它。在加载模块时,可以使用类似于下面的命令来传递参数:
```c
insmod my_module.ko my_value=10
```
这将使用值 10 来设置 my_value 参数的初始值。现在,我们已经知道了 module_param 的基本工作原理,让我们来看看它的实现细节。
使用module_param传递参数的实现细节
在使用 module_param 时,我们需要定义一个符号,用于告诉内核模块加载器模块的指定参数。这可以通过两种方式来实现:
1. 传递参数的值,例如:
```c
modprobe my_module my_param=1
```
2. 传递参数值的地址,例如:
```c
insmod my_module.ko my_param_addr=0x12345678
```
为了更好地理解 module_param 如何工作,我们需要更深入地探讨它的实现细节。
在kernel/module.c中定义了 module_param 小函数,我们可以看一下这个函数的实现:
```c
#define _param_check_bounds(sym, val, min, max) \
do { \
if (__builtin_constant_p(min) && __builtin_constant_p(max)) { \
if ((unsigned long)val < (unsigned long)min || \
(unsigned long)val > (unsigned long)max) \
pr_warn("%s: " #sym " value (%ld) outside bounds [%ld,%ld]\n", \
__func__, (long)val, (long)min, (long)max); \
} \
} while (0)
#define param_check_bounds(type, sym, val) \
_param_check_bounds(sym, val, type##_min, type##_max)
#define module_param_named(name, value, type, perm) \
module_param_internal(name, value, type, perm, false)
#define module_param(name, type, perm) \
module_param_named(name, name, type, perm)
#define module_param_internal(name, value, type, perm, isarray) \
...
__MODULE_PARM_TYPE(name) __##name##__##type \
__attribute__((__aligned__(sizeof(__MODULE_PARM_TYPE(name))))); \
__MODULE_PARM_TYPE(name) *const __##name##__param = \
&__##name##__##type; \
static const char __param_str_##name[] \
__used __attribute__((section("___param"),unused)) = \
__stringify(name) "=" name "," __stringify(type) "," __stringify(perm) "," __stringify(isarray) "," __MODULE_STRING_VALUE(__##name##__param); \
static const struct kernel_param_ops __param_ops_##name = { \
.set = param_set_##type, \
.get = param_get_##type \
}; \
module_param_call(MODULE_PARAM_PREFIX, name, &__param_ops_##name, __##name##__param, perm); \
...
#define module_param_array(name, type, nump, perm) \
module_param_named(name, name, __array(type, nump), perm)
```
可以看到,这个函数定义了一些宏,定义了 module_param 内部可调用的小函数,例如:param_set_int、param_set_bool 和 param_set_charp。当一个模块的参数被传递时,它们会调用这些小函数来设置参数的值。
此外,为了支持加载和卸载内核模块,Linux 内核使用了一种称为 module 的运行时机制,这些机制由 modpost 和 mod tools 等工具生成。这些工具通过对内核模块中的参数进行解析和处理,使参数能够在在运行时进行配置。
使用module_param传递参数示例
让我们来看一个使用 module_param 传递参数的例子。在这个例子中,我们将定义一个简单的内核模块,它接收一个整数类型的参数,并将其与模块中的变量相加。
```c
#include
#include
#include
static int my_param = 0;
module_param(my_param, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
static int __init my_module_init (void)
{
printk(KERN_INFO "my_param is %d\n", my_param);
return 0;
}
static void __exit my_module_exit (void)
{
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A Simple Hello World module");
```
编译和安装模块,如下:
```
# make
# insmod my_module.ko my_param=100
```
我们可以看到以下输出:
```
dmesg | grep my_param
[ 5068.819921] my_param is 100
```
这意味着我们已经成功地通过 module_param 传递了参数。
总结
module_param 是一个内核提供的非常有用的函数,可用于在内核模块中定义和处理参数。它允许我们向内核模块传递参数,以便我们的代码能够在不同的环境中更灵活地运行。我们可以基于需求定义可见和不可见的参数,例如:字串、整数和布尔值,根据不同的权限进行访问。
我们建议您在开发Linux设备驱动程序时使用 module_param,因为它可以使您的驱动程序更加灵活和可配置。我们希望这篇文章对您的内核编程有所帮助,让您更好地理解和使用 module_param。