技术知识库

等待队列如何实现字符设备阻塞读写

等待队列如何实现字符设备阻塞读写

摘要

等待队列把“当前条件不满足”的进程挂起,并在设备状态改变时由另一条执行路径唤醒。对 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 被唤醒并输出数据。

关联

待验证

  • 用信号中断一个正在等待的 read(),观察 wait_event_interruptible() 的返回值和用户空间现象。