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




