Linux基础 -- 快速与用户态共享内核字符串读写之 module_param_string

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");

用法

  1. 编译成模块(Makefile 里 obj-m += demo_param.omake -C /lib/modules/$(uname -r)/build M=$PWD modules 这种标准 out-of-tree 方法就行)。

  2. 以默认值加载:

    insmod demo_param.ko
    dmesg | tail
    # demo_param: module loaded. user_msg="hello_from_kernel"
    
  3. 模块加载时传入自定义字符串(命令行参数):

    insmod demo_param.ko user_msg="panic_detector_armed"
    dmesg | tail
    # demo_param: module loaded. user_msg="panic_detector_armed"
    
  4. 运行时在 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
    
  5. 卸载:

    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 数组)。

注意两点:

  1. 写发生在 sysfs 上下文,会持有参数框架的互斥来避免并发破坏;你在读的时候不需要特地加锁,一般来说字符串更新是原子的“整个缓冲区复制+NUL结尾”,不会出现半写半旧的乱序指针(尤其是 module_param_string 这种固定数组)。如果你需要极强一致性,可以自己加 rcu_read_lock()/spinlock 级保护,但大多数场景不需要。
  2. 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调用链”。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值