小白学Linux之module_param()函数学习

本文详细介绍了Linux内核中module_param()宏的使用方法及原理,包括如何在内核模块中定义和使用不同类型的参数,并提供了一个具体的示例程序。

module_param()的学习

一.module_param
1.为什么引入
在用户态下编程可以通过main()来传递命令行参数,而编写一个内核模块则可通过module_param()来传递命令行参数.
2. module_param宏是Linux 2.6内核中新增的,该宏被定义在include/linux/moduleparam.h文件中,具体定义如下:
/* Helper functions: type is byte, short, ushort, int, uint, long,
ulong, charp, bool or invbool, or XXX if you define param_get_XXX,
param_set_XXX and param_check_XXX.

*/
#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(name, type, perm)
module_param_named(name, name, type, perm)
由此可知 module_param的实现是通过module_param_named(name, name, type, perm)的。

3.module_param使用了3个参数:变量名,它的类型,以及一个权限掩码用来做一个辅助的sysfs入口。
这个宏定义应当放在任何函数之外,典型地是出现在源文件的前面。
eg: static char *whom="world"
static int tige=1;
module_param(tiger,int,S_IRUGO);
module_param(whom,charp,S_IRUGO);

4.模块参数支持许多类型:
bool
invbool
一个布尔型( true 或者 false)值(相关的变量应当是 int 类型). invbool 类型颠倒了值, 所以真值变成 false, 反之亦然.
charp :一个字符指针值. 内存为用户提供的字串分配, 指针因此设置.
int
long
short
uint
ulong
ushort
基本的变长整型值. 以 u 开头的是无符号值.

5.数组参数, 用逗号间隔的列表提供的值, 模块加载者也支持。

声明一个数组参数, 使用:
module_param_array(name,type,num,perm);
这里 name 是你的数组的名子(也是参数名),
type 是数组元素的类型,
num 是一个整型变量,
perm 是通常的权限值.
如果数组参数在加载时设置, num 被设置成提供的数的个数. 模块加载者拒绝比数组能放下的多的值.

Tiger-John说明:

perm参数的作用是什么?
最后的 module_param 字段是一个权限值,表示此参数在sysfs文件系统中所对应的文件节点的属性。你应当使用 <linux/stat.h> 中定义的值. 这个值控制谁可以存取这些模块参数在 sysfs 中的表示.当perm为0时,表示此参数不存在 sysfs文件系统下对应的文件节点。 否则, 模块被加载后,在/sys/module/ 目录下将出现以此模块名命名的目录, 带有给定的权限.。
权限在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
使用 S_IRUGO 参数可以被所有人读取, 但是不能改变; S_IRUGO|S_IWUSR 允许 root 来改变参数. 注意, 如果一个参数被 sysfs 修改, 你的模块看到的参数值也改变了, 但是你的模块没有任何其他的通知. 你应当不要使模块参数可写, 除非你准备好检测这个改变并且因而作出反应.

二实例:

说了这么多,看一个程序体验以下:

1.module_param.c

/* * file name : module_param.c * author : tiger-John */ #include<linux/init.h> #include<linux/module.h> #include<linux/kernel.h> MODULE_LICENSE("GPL"); static char *who; static int times; module_param(who,charp,0644); module_param(times,int,0644); static int __init hello_init(void) { int i; for(i = 1;i <= times;i++) printk("%d %s!/n",i,who); return 0; } static void __exit hello_exit(void) { printk("Goodbye,%s!/n",who); } module_init(hello_init); module_exit(hello_exit);

2.编写Makefile文件

1 obj-m:=module_param.o 2 CURRENT_PATH:=$(shell pwd) 3 VERSION_NUM :=$(shell uname -r) 4 LINUX_PATH :=/usr/src/linux-headers-$(VERSION_NUM) 5 6 all : 7 make -C $(LINUX_PATH) M=$(CURRENT_PATH) modules 8 clean : 9 make -C $(LINUX_PATH) M=$(CURRENT_PATH) clean

3.在终端输入:make

4 .加载模块: sudo insmdo module_param.ko who=tiger times=4

5.dmesg :查看结果。

过程实例:

a.在终端输入:make

think@ubuntu:~/module_param$ make
make -C /usr/src/linux-headers-2.6.32-25-generic M=/home/think/module_param modules
make[1]: 正在进入目录 `/usr/src/linux-headers-2.6.32-25-generic'
Building modules, stage 2.
MODPOST 1 modules
make[1]:正在离开目录 `/usr/src/linux-headers-2.6.32-25-generic'
think@ubuntu:~/module_param$

b.在终端输入: sudo insmod module_param.ko who=tiger times=4

think@ubuntu:~/module_param$ sudo insmod module_param.ko who=tiger times=4

c 在终端输入:dmesg

[ 4297.711137] 1 tiger!
[ 4297.711139] 2 tiger!
[ 4297.711140] 3 tiger!
[ 4297.711141] 4 tiger!


<think>我们正在处理关于Linux内核中module_param_named函数的问题。根据引用内容,我们可以总结如下: 引用[1]提到:module_param_named是一个用于定义模块参数的宏,允许为模块参数指定一个外部名称,而内部变量可以使用不同的名称。这在需要将内部变量名与用户提供的参数名区分开来时非常有用。 引用[2]介绍了module_param()函数,它用于在内核模块中传递命令行参数。虽然这里没有直接讲module_param_named,但我们可以推断module_param_named是module_param的一种扩展形式,允许使用不同的外部名称。 引用[3]展示了一个内核模块的例子,其中使用了module_parammodule_param_array,但没有使用module_param_named。不过,我们可以根据module_param的使用方式来推断module_param_named的使用。 因此,我们可以这样回答: module_param_named宏的作用是允许模块参数在用户空间(通过sysfs或模块加载时的命令行)使用的名称与模块内部实际使用的变量名称不同。 使用方法: 宏原型(根据引用[1]和[2]的类似描述): ```c module_param_named(外部名称, 内部变量, 类型, 权限掩码); ``` 参数说明: - 外部名称:在用户空间(如insmod或modprobe命令行,或/sys/module/模块名/parameters/下)可见的参数名。 - 内部变量:模块内部使用的变量名。 - 类型:参数的数据类型,如int, charp(字符指针), bool等。 - 权限掩码:指定在sysfs文件系统中的访问权限(例如S_IRUSR表示用户可读)。 示例: 假设我们有一个内部变量名为debug_level,但我们希望用户使用“debug”作为参数名。我们可以这样定义: ```c static int debug_level = 0; module_param_named(debug, debug_level, int, S_IRUSR | S_IWUSR); ``` 这样,用户可以通过`insmod mymodule.ko debug=5`来设置debug_level的值,而在模块内部我们使用debug_level变量。 注意事项: 1. 权限掩码:通常使用<linux/stat.h>中定义的权限位,如S_IRUGO(所有人可读)、S_IWUSR(用户可写)等。 2. 类型:与module_param支持的类型相同,包括基本类型(int, long, ushort等)和特定类型(如charp表示字符串)。 3. 对于数组参数,可以使用module_param_array_named(如果有的话,但标准内核可能没有直接提供,通常使用module_param_array并配合命名则需另做处理,不过我们可以通过module_param_named的方式传递数组?实际上,引用[3]中使用了module_param_array,它接受一个数组。而module_param_named通常用于单个变量。如果需要数组的命名,可能需要其他方法,但这不是我们这里的问题重点)。 相关问题: 1. module_param_named和module_param有什么区别? 2. 如何在内核模块中传递字符串数组参数? 3. 模块参数的权限掩码有哪些可选值?它们分别代表什么权限?</think>### Linux内核中`module_param_named`函数详解 `module_param_named`是Linux内核模块编程中用于定义模块参数的关键宏,它允许开发者为模块参数指定一个**外部可见名称**,同时内部使用不同的变量名。这种设计增强了代码的灵活性和可维护性。 #### 核心作用 1. **名称解耦** 内部变量名与用户空间参数名分离,例如内部变量叫`internal_debug`,用户可通过`ext_debug`名称传递参数。 ```c static int internal_debug; module_param_named(ext_debug, internal_debug, int, 0644); ``` 加载模块时使用: `insmod mymodule.ko ext_debug=1` 2. **兼容性维护** 当内部变量名变更时,无需修改用户空间接口,保持向后兼容性[^1]。 3. **参数可见性控制** 通过权限掩码(如`0644`)控制`/sys/module/<模块名>/parameters/`下的读写权限。 #### 使用方法 **函数原型**(定义于`include/linux/moduleparam.h`): ```c module_param_named(external_name, internal_var, type, perm_mask); ``` **参数说明**: | 参数 | 类型 | 作用 | 示例值 | |---------------|-----------|-------------------------------|---------------| | `external_name` | 字符串 | 用户空间可见的参数名 | `"debug_mode"` | | `internal_var` | 变量 | 模块内部接收参数的变量 | `dbg_level` | | `type` | 数据类型 | 参数类型(int/charp/bool等) | `int` | | `perm_mask` | 权限掩码 | sysfs文件权限位 | `0644` | **完整示例**: ```c #include <linux/moduleparam.h> static char *msg = "default"; static int debug_level; // 定义两个命名参数 module_param_named(message, msg, charp, 0644); // 用户通过message传递字符串 module_param_named(debug, debug_level, int, 0600); // 用户通过debug传递整数 static int __init my_init(void) { printk("Message: %s\n", msg); printk("Debug Level: %d\n", debug_level); return 0; } ``` 加载模块时传递参数: `insmod mymodule.ko message="Hello" debug=3` #### 权限掩码说明 | 权限值 | 含义 | 典型场景 | |---------|----------------------|------------------------| | `0644` | 所有用户可读,仅root可写 | 普通配置参数 | | `0600` | 仅root可读写 | 敏感参数(如调试开关) | | `0444` | 所有用户只读 | 只读状态参数 | > ⚠️ **注意**: > 1. 字符串参数必须使用`charp`类型(字符指针) > 2. 权限掩码需包含`S_I`前缀(如`S_IRUGO`),实际开发中直接使用八进制更便捷[^2][^3] #### 与`module_param`的区别 | 特性 | `module_param` | `module_param_named` | |---------------------|-------------------------|----------------------------| | 参数名 | 内部变量名即外部参数名 | 可自定义外部参数名 | | 代码侵入性 | 低 | 高(需维护双名称) | | 接口兼容性 | 变量改名即破坏接口 | 内部改名不影响外部接口 | | 典型应用场景 | 简单模块 | 公共API/长期维护模块 |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值