技术知识库

poll 与 epoll 如何通过等待队列发现设备就绪

poll 与 epoll 如何通过等待队列发现设备就绪

摘要

字符设备的 .poll 回调不读取数据,也不替用户程序调用 read()。它通过 poll_wait() 把当前文件和设备等待队列建立联系,再根据 KFIFO 当前状态返回 EPOLLIN、EPOLLOUT 等就绪掩码;设备状态变化后,驱动唤醒对应等待队列,poll/epoll 核心据此把具体文件事件返回给用户空间。

.poll 做两件事

static __poll_t device_poll(struct file *file, poll_table *wait)
{
	struct kfifo_device *device = file->private_data;
	__poll_t mask = 0;

	poll_wait(file, &device->read_queue, wait);
	poll_wait(file, &device->write_queue, wait);

	if (!kfifo_is_empty(&device->misc_kfifo))
		mask |= EPOLLIN | EPOLLRDNORM;
	if (!kfifo_is_full(&device->misc_kfifo))
		mask |= EPOLLOUT | EPOLLWRNORM;

	return mask;
}

第一件事是登记等待关系;第二件事是报告调用当下的状态。真正的数据复制仍由后续 read() 或 write() 完成。

为什么能定位 8 个设备中的一个

每个设备对象都有独立的 KFIFO 和等待队列:

fd0 → device0 → read_queue0
fd1 → device1 → read_queue1
...
fd7 → device7 → read_queue7

写入设备 3 时,驱动只唤醒 read_queue3。epoll 注册在该队列中的回调把 fd3 对应的事件放入就绪集合;用户空间从 epoll_wait() 返回的 epoll_event.data 识别事件源,再决定读取哪个 fd。

poll、epoll 与 FASYNC 的边界

  • poll/epoll:用户线程主动调用等待接口,内核返回就绪 fd。
  • FASYNC:内核通过 SIGIO 主动通知事先登记的进程。
  • 两者都只通知“可能可以读/写”,数据仍由用户程序调用 read()/write() 传输。
  • 一个文件没有必要同时使用两套通知机制,除非应用有明确设计需求。

证据与来源

  • 实验手册 v2.0.7,第 6.7 节。
  • 当前实现:/home/martin-zou/workspace/gcc-lab/char_mod/epollkfifo_mailbox/epollkfifo_mailbox.c。
  • Linux 5.0:fs/eventpoll.c::ep_ptable_queue_proc()、ep_poll_callback()。
  • 当前模块已经完成 ARM64 交叉编译;目标系统上的 8 节点和 epoll 用户程序尚未完成验证。

关联

待验证

  • 编写用户态 epoll 程序,同时监听 8 个节点,逐个写入并核对返回的事件源。