module_param / module_param_string 暴露一个字符串参数,让用户既能在加载模块时传参,也能在运行时通过 sysfs 修改该字符串;
1. 最推荐的方式:module_param_string
module_param_string 适合“用固定长度的 char buffer 保存字符串”的场景,尤其是嵌入式/内存敏感系统,因为它不会在每次写入时动态分配/释放内存。
代码示例
#include <linux/module.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h> // pr_info()
#define MSG_MAXLEN 64
/* 这个buffer里放我们想暴露给用户态的字符串参数 */
static char user_msg[MSG_MAXLEN] = "hello_from_kernel";
/*
* module_param_string(name, string, len, perm)
* name : 出现在/sys/module/<modname>/parameters/ 下的参数名
* string: 实际存储字符串的缓冲区
* len : 缓冲区长度
* perm : sysfs 权限,0644 表示root可写,其他人可读
*/
module_param_string(user_msg, user_msg, MSG_MAXLEN, 0644);
MODULE_PARM_DESC(user_msg, "User-controlled message string");
/* 为了 demo,我们在加载和卸载时打印一下这个字符串 */
static int __init demo_init(void)
{
pr_info("demo_param: module loaded. user_msg=\"%s\"\n", user_msg);
return 0;
}
static void __exit demo_exit(void)
{
pr_info("demo_param: module exiting. user_msg=\"%s\"\n", user_msg);
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("you");
MODULE_DESCRIPTION("demo: string module_param example");
用法
-
编译成模块(Makefile 里
obj-m += demo_param.o,make -C /lib/modules/$(uname -r)/build M=$PWD modules这种标准 out-of-tree 方法就行)。 -
以默认值加载:
insmod demo_param.ko dmesg | tail # demo_param: module loaded. user_msg="hello_from_kernel" -
模块加载时传入自定义字符串(命令行参数):
insmod demo_param.ko user_msg="panic_detector_armed" dmesg | tail # demo_param: module loaded. user_msg="panic_detector_armed" -
运行时在 sysfs 里读/写:
# 读 cat /sys/module/demo_param/parameters/user_msg # panic_detector_armed # 写(64字节上限,超了会被截断) echo "new_runtime_value" > /sys/module/demo_param/parameters/user_msg # 验证写进内核变量了 cat /sys/module/demo_param/parameters/user_msg # new_runtime_value -
卸载:
rmmod demo_param dmesg | tail # demo_param: module exiting. user_msg="new_runtime_value"
到这里你就完成了一个“可运行时更新的内核字符串参数”。
2. 另一种方式:module_param(..., charp, ...)
module_param 也可以配合类型 charp 来导出字符串指针。例如:
static char *cfg_str = "default_cfg";
/*
* module_param(var, type, perm)
* var : 变量名 (这里是char * 指针)
* type : charp 表示字符串
* perm : sysfs 权限
*
* 对 charp 参数来说,内核的参数处理器会在写入时为你分配新的内存并更新指针,
* 旧的会被 kfree(),所以它是动态分配的。
*/
module_param(cfg_str, charp, 0644);
MODULE_PARM_DESC(cfg_str, "dynamic string pointer param (charp)");
它的行为也很像上面那个例子:
insmod demo.ko cfg_str="abc"会覆盖默认值;- 运行时可以
echo xyz > /sys/module/demo/parameters/cfg_str动态修改。
区别在于内存模型:
charp这种方式会在运行时用kstrdup()/kfree()改指针,属于动态分配,适合 PC/server,不太适合内存极紧、强调确定性/无fragment的场景。module_param_string用的是你自己提供的固定栈/静态数组,大小可控,不会碎片化。所以嵌入式、RT-ish 系统我更推荐module_param_string。
3. 权限位 (perm) 怎么选
module_param* 系列里的最后一个参数是 sysfs 权限,常用值:
0444:只读(运行期不能写,只能在模块加载/内核启动参数里设)0644:root 可写,其他可读。大部分调参参数会用这个0600:只有 root 可读写(比如有敏感信息)
如果你用 0444,那 echo newval > /sys/module/.../parameters/... 会失败,从而把这个参数变成“load-time only”。这是做只读调试标志/版本号的常见策略。
4. 从模块代码内部读取这个字符串
就是直接用变量本身。
在上面的 module_param_string 例子里,user_msg 就是一个普通的全局数组:
pr_info("current user_msg = %s\n", user_msg);
在中断上下文、工作队列、内核线程、ftrace回调里都可以随时读它(访问它就像访问普通的全局 char 数组)。
注意两点:
- 写发生在 sysfs 上下文,会持有参数框架的互斥来避免并发破坏;你在读的时候不需要特地加锁,一般来说字符串更新是原子的“整个缓冲区复制+NUL结尾”,不会出现半写半旧的乱序指针(尤其是
module_param_string这种固定数组)。如果你需要极强一致性,可以自己加rcu_read_lock()/spinlock 级保护,但大多数场景不需要。 module_param_string的实现是按len限制拷贝并保证 NUL 结尾,超长写入会被截断。也就是说不会越界写爆user_msg[],这是它比手工charp更安全的点。
5. 总结
-
用
module_param_string(name, buf, buflen, perm)是最干净稳妥的方式去导出“可读可写的字符串参数”。 -
把
perm设成0644,就可以运行时通过/sys/module/<modname>/parameters/<name>动态更新。 -
这对你的调试/诊断非常有用:
- 你可以在不重启系统、不重载模块的情况下,往内核里塞一段“当前session标签/trace过滤关键字/触发阈值表达式”之类的字符串。
- 你的内核逻辑在关键路径里随时读这个字符串来决定行为,比如“只抓这个会话ID的ftrace调用链”。

被折叠的 条评论
为什么被折叠?



