技术知识库

每次 open 分配独立状态导致写者无法唤醒读者

每次 open 分配独立状态导致写者无法唤醒读者

摘要

实验 6-8 故意把 KFIFO 和等待队列放入每次 open() 新分配的私有对象。cat 和 echo 分别打开设备,得到两个不同对象;读者睡在队列 A,写者却写入 FIFO B、唤醒队列 B,因此读者无法继续。当前已经完成源码层面的根因回答,尚未在目标系统故意复现挂起现象。

环境

参考目录:kmodules/rlk_lab/rlk_basic/chapter_6_device_driver/lab8_mydemodrv_find_error
参考源码:mydemodrv_error.c
内核:Running Linux Kernel 5.0 ARM64

故意错误的状态归属

struct mydemo_private_data {
	struct mydemo_device *device;
	char name[64];
	struct kfifo mydemo_fifo;
	wait_queue_head_t read_queue;
	wait_queue_head_t write_queue;
};

每次 open() 都执行 kmalloc()、kfifo_alloc() 和等待队列初始化:

cat open  → private A → fifo A、read_queue A
echo open → private B → fifo B、read_queue B

根因

wake_up_interruptible() 只唤醒调用时传入的那一个等待队列。内核不会根据设备节点名称推断队列 A 和队列 B 应当属于同一个业务设备。

因此:

cat:  wait_event_interruptible(read_queue A, fifo A 非空)
echo: kfifo_from_user(fifo B)
      wake_up_interruptible(read_queue B)

写者确实执行了唤醒,但唤醒了错误的对象。

正确设计方向

每次打开可以继续拥有独立的 struct file 和少量私有上下文,但 KFIFO 与等待队列应放在模块初始化期间创建的共享 struct mydemo_device 中。两个打开者的 private_data 最终都引用同一个设备对象。

结论

此前给出的“生产者和消费者必须指向同一个共享设备状态”结论正确。更精确地说,决定条件是对象身份和指针关系,而不是内存连续性或注册代码是否写在一起。

关联

待验证

  • 在目标系统运行故意错误版本,记录读者睡眠、写者唤醒另一队列的地址与 PID。