### module_param 宏详解
#### 一、概述
在Linux内核编程中,为了实现对模块参数的管理和访问,内核提供了一套完善的机制。`module_param` 是一个非常重要的宏,它允许开发者向内核模块传递参数,并且能够通过 sysfs 文件系统访问这些参数。这对于调试和配置内核模块具有重要意义。
#### 二、`module_param` 的定义与作用
`module_param` 宏定义在 `include/linux/moduleparam.h` 文件中,其定义如下:
```c
#define module_param(name, type, perm) \
module_param_named(name, name, type, perm)
```
这个宏接受三个参数:
1. **name**:要传递的参数变量名。
2. **type**:变量的数据类型。
3. **perm**:访问参数的权限。
#### 三、权限参数 `perm`
`perm` 参数用于指定参数在 sysfs 文件系统中所对应的文件节点的权限。这些权限值来源于 `<linux/stat.h>` 头文件。例如:
- `S_IRUSR`: 用户读权限。
- `S_IWUSR`: 用户写权限。
- `S_IXUSR`: 用户执行权限。
- `S_IRGRP`: 组读权限。
- `S_IWGRP`: 组写权限。
- `S_IXGRP`: 组执行权限。
- `S_IROTH`: 其他用户读权限。
- `S_IWOTH`: 其他用户写权限。
- `S_IXOTH`: 其他用户执行权限。
#### 四、权限参数的应用示例
假设我们想要创建一个参数,使得所有用户都可以读取它,但只有 root 用户可以修改它,我们可以这样定义:
```c
module_param(howmany, int, S_IRUGO | S_IWUSR);
```
这里 `S_IRUGO` 表示所有用户(user, group, other)都可以读取该参数,而 `S_IWUSR` 表示仅 root 用户可以写入。这样,当我们加载模块后,在 `/sys/module/模块名/parameters/howmany` 路径下就可以找到这个参数。
#### 五、`module_param` 支持的参数类型
`module_param` 支持多种数据类型的参数,包括但不限于:
1. **bool**:布尔型值,相关变量应为 `int` 类型。真值为 1,假值为 0。
2. **invbool**:与 `bool` 相反,真值为 0,假值为 1。
3. **charp**:字符指针类型,指向用户提供的字符串。
4. **int** / **long** / **short** / **uint** / **ulong** / **ushort**:整型数值类型,带 `u` 前缀的是无符号类型。
#### 六、数组参数的支持
除了单个参数外,`module_param` 还支持数组参数。可以通过以下宏定义数组参数:
```c
module_param_array(name, type, num, perm);
```
这里的参数含义如下:
- **name**:数组的名称,同时也是参数名。
- **type**:数组元素的数据类型。
- **num**:整型变量,用来记录数组元素的数量。
- **perm**:权限值。
例如,如果我们想要定义一个整型数组参数,可以这样定义:
```c
static int my_array[10];
static int my_array_num = 0;
module_param_array(my_array, int, my_array_num, S_IRUGO);
```
加载模块时提供的值数量将赋给 `my_array_num` 变量,如果提供的值超过数组容量,模块加载器会拒绝加载。
#### 七、实际应用案例
下面是一个简单的模块 `hello.c`,它演示了如何使用 `module_param`:
```c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
MODULE_LICENSE("Dual BSD/GPL");
static char *who = "world";
static int times = 1;
module_param(times, int, S_IRUSR);
module_param(who, charp, S_IRUSR);
static int hello_init(void) {
int i;
for (i = 0; i < times; i++) {
printk(KERN_ALERT "(%d)hello, %s!\n", i, who);
}
return 0;
}
static void hello_exit(void) {
// 退出处理
}
module_init(hello_init);
module_exit(hello_exit);
```
在这个例子中,`hello.c` 模块定义了两个参数:`who` 和 `times`。`who` 是一个字符串,`times` 是一个整数。这两个参数都可以被用户通过 sysfs 文件系统进行访问和修改。
#### 八、总结
`module_param` 宏在 Linux 内核开发中扮演着极其重要的角色。它不仅提供了方便的接口来处理模块参数,还确保了参数的安全性和灵活性。通过合理利用 `module_param`,开发者可以轻松地实现模块参数的配置和调试,从而提高模块的可用性和扩展性。