You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Linux内核模块中是否需用互斥锁?多进程并发写设备分析

关于Linux内核模块dev_write并发访问的问题

嘿,这个问题问到内核模块开发的一个常见坑点了——并发访问的处理,咱们一步步来捋清楚:

1. 两个进程同时写入时会发生什么?

你的dev_write函数里没有任何同步机制,而Linux内核是抢占式多任务的。这意味着当两个进程同时调用这个函数时,CPU会在两个进程的循环执行过程中频繁切换:

  • 进程A刚打印完buffer[0],CPU可能就切换到进程B,打印它的buffer[0]
  • 接着又切回进程A打印buffer[1],再切到进程B打印buffer[1]

最终你在dmesg里看到的输出会是两个进程的字符交错在一起,完全不是每个进程单独write的连续输出。

另外,你的函数直接返回0,这会告诉用户空间进程“这次write只成功写入了0字节”,不管实际循环执行了多少次——这本身就是不符合预期的错误行为。

2. 能否正常执行?

严格来说,不会“正常执行”

  • 从功能上看,输出完全混乱,返回值错误,不符合用户对write操作的预期(用户希望一次write的所有数据被连续处理)
  • 还有个隐藏风险:你直接解引用用户空间的指针buffer[i],这在内核里是绝对禁止的!用户空间的内存可能随时被换出、释放,直接访问会触发页错误,严重的话会导致内核panic。正确的做法是用copy_from_user函数安全地把用户空间数据拷贝到内核缓冲区再处理。

如果只说“会不会崩溃”,在某些幸运的情况下可能不会直接panic,但行为完全不符合预期,根本算不上“正常执行”。

3. dev_open时需要加互斥锁吗?此场景下该怎么处理?

不需要在dev_open里加互斥锁,除非你的设备设计成“只能被一个进程打开”(这种场景很少见)。

如果想要让每个进程的write操作是原子性的(即一次write的所有字符连续打印,不被其他进程打断),正确的做法是:

  • 在模块里定义一个全局的互斥锁(比如struct mutex dev_mutex;),在模块初始化时用mutex_init(&dev_mutex);初始化
  • dev_write函数的开头调用mutex_lock(&dev_mutex);获取锁,在函数返回前调用mutex_unlock(&dev_mutex);释放锁

这样就能保证同一时间只有一个进程执行dev_write里的循环,输出不会交错。

举个修正后的代码片段:

#include <linux/mutex.h>

struct mutex dev_mutex;

static int __init dev_init(void) {
    // 其他初始化操作...
    mutex_init(&dev_mutex);
    return 0;
}

static ssize_t dev_write(struct file *filep, const char __user *buffer, size_t len, loff_t *offset) {
    size_t i;
    char kernel_buf[256]; // 假设一次最多处理256字节,根据实际需求调整
    size_t copy_len = min(len, sizeof(kernel_buf));

    // 安全拷贝用户空间数据到内核
    if (copy_from_user(kernel_buf, buffer, copy_len)) {
        return -EFAULT;
    }

    mutex_lock(&dev_mutex);
    for (i = 0; i < copy_len; ++i) {
        printk(KERN_INFO "buffer[%zu] is '%c'\n", i, kernel_buf[i]);
    }
    mutex_unlock(&dev_mutex);

    return copy_len; // 返回实际处理的字节数
}

总结一下:你的原代码存在并发导致的输出混乱、错误返回值、不安全的用户空间访问三个问题,解决并发的核心是在dev_write里加互斥锁,而不是在dev_open里。

内容的提问来源于stack exchange,提问作者kylemart

火山引擎 最新活动