技术知识库
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 用户程序尚未完成验证。
关联
- 字符设备驱动 MOC
- 等待队列如何实现字符设备阻塞读写
- 共享设备状态与每次 open 私有状态如何决定进程能否互相唤醒
- 为八个 KFIFO 设备添加 poll 支持
- FASYNC 与 SIGIO 如何实现字符设备异步通知
待验证
- 编写用户态 epoll 程序,同时监听 8 个节点,逐个写入并核对返回的事件源。