技术知识库
等待队列如何实现字符设备阻塞读写
等待队列如何实现字符设备阻塞读写
摘要
等待队列把“当前条件不满足”的进程挂起,并在设备状态改变时由另一条执行路径唤醒。对 KFIFO 字符设备而言,读者等待“FIFO 非空”,写者等待“FIFO 非满”;wait_event_interruptible() 负责睡眠和重新检查条件,wake_up_interruptible() 只负责通知,醒来的进程仍必须重新判断条件。
问题/背景
KFIFO 只管理数据,不会自动决定 FIFO 为空时 read() 应立即失败还是睡眠。实验 6-6 在 Nonblocking I/O 的基础上加入等待队列,使普通方式打开的设备具备阻塞读写语义。
读路径
if (kfifo_is_empty(&fifo)) {
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
}
ret = wait_event_interruptible(read_queue,
!kfifo_is_empty(&fifo));
if (ret)
return ret;
FIFO 为空时,普通读者进入可中断睡眠。写者放入数据后调用:
wake_up_interruptible(&read_queue);
读者被调度后重新检查 !kfifo_is_empty(),条件成立才继续复制数据。
写路径
写者的条件与读者对称:
if (kfifo_is_full(&fifo)) {
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
}
ret = wait_event_interruptible(write_queue,
!kfifo_is_full(&fifo));
读者消费数据、FIFO 出现空间后唤醒写者:
wake_up_interruptible(&write_queue);
等待队列不是数据容器
等待队列保存的是等待关系,KFIFO 保存的才是业务数据。必须保证条件和等待队列属于同一个共享设备对象;否则读者可能睡在队列 A,而写者唤醒队列 B,详见 共享设备状态与每次 open 私有状态如何决定进程能否互相唤醒。
中断与返回值
wait_event_interruptible() 可能因为信号返回非零值,驱动应把该错误返回给用户空间。它不是一次睡眠后无条件继续执行;宏内部会围绕入队和调度反复检查条件,避免把一次已经发生的状态变化简单遗漏。
证据与来源
- 实验手册 v2.0.7,第 6.6 节。
- 实验内核:Running Linux Kernel 5.0,ARM64,
5.0.0+。 - 当前实现:
/home/martin-zou/workspace/gcc-lab/char_mod/blockkfifo_mailbox/blockkfifo_mailbox.c。 - 实际验证:FIFO 为空时
cat /dev/blockkfifo_mailbox等待;写入后cat被唤醒并输出数据。
关联
- 字符设备驱动 MOC
- KFIFO 如何用环形缓冲区管理字符设备数据
- O_NONBLOCK 如何改变字符设备读写语义
- 共享设备状态与每次 open 私有状态如何决定进程能否互相唤醒
- 使用等待队列实现阻塞 KFIFO 邮箱
待验证
- 用信号中断一个正在等待的
read(),观察wait_event_interruptible()的返回值和用户空间现象。