pollfd

关注
义项:系统调用数据结构
pollfd

pollfd是类 Unix 操作系统中用于 I/O 多路复用的一个结构体类型,在 poll 系统调用中用来描述单个待监视的文件描述符及其关注的事件和实际发生的事件[1]。它定义在头文件 poll.h 中,包含 fd、events、revents 三个成员,应用程序只需构造一个 pollfd 数组并把数组长度传给 poll,即可同时等待多个文件描述符上的 I/O 就绪状态[2]。相较于把输入输出合并在同一参数中的 select,pollfd 把关注事件与实际发生事件分成两个字段,是理解 Linux 等系统 I/O 复用机制的基础[3]。

百科 图文
目录
  1. 定义
  2. 原理
  3. 发展历程
  4. 应用
  5. 局限
  6. 参见

定义

pollfd 是定义在 poll.h 头文件中的一个结构体类型,用来描述一次轮询请求所涉及的一个文件描述符[4]。其成员为 int 型的 fd、short 型的 events 与 short 型的 revents,分别对应被轮询的描述符、应用程序关注的输入事件标志以及内核实际返回的事件标志[1]。

pollfd 本身不执行任何动作,它只是 poll 系统调用的参数元素。调用者需要自行分配一个 pollfd 数组,并把数组的元素个数与数组首地址一并传入 poll,由内核逐个检查数组中的描述符[5]。数组元素的个数由 nfds_t 类型的 nfds 参数给出,该类型在 poll.h 中通过 typedef 定义为一个无符号整数类型[4]。

原理

events 是输入字段,其取值由若干事件位按位或组合而成,用来告诉内核应用程序对 fd 上的哪些事件感兴趣;若该字段为 0,则该描述符的所有事件都被忽略[6]。revents 是输出字段,由内核在返回前填写,其中既可能包含 events 中请求过的事件位,也可能包含 POLLERR、POLLHUP、POLLNVAL 这 3 个无需在 events 中声明的位[7]。

常用的事件位包括表示可无阻塞读取普通数据的 POLLIN、表示有紧急数据可读的 POLLPRI、表示可无阻塞写入的 POLLOUT,以及仅在 revents 中出现的 POLLERR、POLLHUP 和 POLLNVAL[1]。当 fd 被置为负值时,内核会忽略该元素对应的 events,并将其 revents 置 0,这一约定使程序无需重建数组即可在轮询集合中临时屏蔽某个描述符[7]。

在 Linux 内核实现中,poll 的入口会按页为单位构建 poll_list 链表,把用户空间的 pollfd 数组分批复制到内核空间,随后对每个元素调用 do_pollfd,通过文件对象上的 poll 回调注册等待队列并取得掩码,最后把结果由 revents 字段写回用户空间[8]。内核在汇总结果时会把掩码与 events、POLLERR、POLLHUP 相与,因此未被请求的事件不会出现在 revents 中[9]。

发展历程

poll 接口源自 System V 的轮询机制,glibc 中的 poll.h 至今仍标注为与 System V poll 接口兼容的定义[10]。在早期的 SVR3 实现中,无法通过把描述符设为 -1 来让内核跳过数组中的某一项,SVR4 修正了这一限制[11]。

Linux 于 1997 年发布的 2.1.23 版内核中引入了 poll 系统调用,其动机之一是满足 Digital UNIX 二进制兼容的需要;在该版本中,内核的原生内部接口反而更接近 poll,select 被改为在该接口之上实现[12]。与之配套的库函数在 libc 5.4.28 中提供,当内核尚未提供该系统调用时会用 select 进行模拟[7]。

此后 pollfd 与相关事件常量被纳入 POSIX 等标准,poll.h 需定义该结构体、nfds_t 类型以及一组可用按位或组合的事件符号常量[2]。Linux 2.6.17 及以后的版本还在 events 中增加了 POLLRDHUP,用于表示流式套接字的对端已关闭连接或关闭了写方向,其定义需要启用 _GNU_SOURCE 特性测试宏[6]。

应用

pollfd 数组最典型的用途是编写同时处理多个连接的服务器程序。程序把监听套接字与所有已建立连接的套接字填入 pollfd 数组,一次 poll 调用返回后遍历数组,凡是 revents 非零的元素即为就绪描述符,可以直接对其执行读取或写入[3]。

由于 poll 使用显式的 pollfd 数组而不是固定大小的位图,它能监视的描述符数量在本质上没有上限,只受进程可打开文件数的约束,这一点优于受 FD_SETSIZE 限制的 select[11]。此外,把关注事件与返回事件分离在 events 和 revents 两个字段中,意味着在循环中反复调用 poll 时不必像 select 那样每次重新初始化描述符集合[5]。

在设备驱动层面,驱动程序通过实现文件操作中的 poll 回调来支持 pollfd 机制,回调中通常调用 poll_wait 把自身的等待队列登记到内核传递进来的 poll_table 上,从而在该设备状态变化时唤醒等待的进程[13]。

局限

poll 的性能随被监视描述符数量的增长而下降。每次调用都需要把 pollfd 数组从用户空间复制到内核空间,内核再逐个检查其中的每个描述符,返回后应用程序还必须遍历整个数组才能找出就绪项[3]。即使两次调用之间监视的集合完全相同,内核也不会保留上一次的登记结果,这些开销需要重复付出[3]。

pollfd 中 fd 与 revents 的实际宽度为 16 位,可编码的事件位数量因此受限,事件掩码的扩展空间有限[9]。此外,poll 的超时精度以毫秒为单位,低于 select 的微秒级精度,虽然两者的精度都受系统时钟粒度的限制[11]。为了解决大量描述符场景下的扩展性问题,后续出现了让内核长期记录进程所关注描述符的 epoll 等机制[3]。

参见

  • poll 系统调用 —— 使用 pollfd 数组等待多个文件描述符就绪的系统调用。

  • select 系统调用 —— 与 poll 功能相近的 I/O 多路复用接口,使用固定大小的描述符位图。

  • epoll —— Linux 上为大量描述符场景设计的 I/O 事件通知机制。

  • 文件描述符 —— pollfd 的 fd 字段所引用的操作系统资源标识。

  • I/O 多路复用 —— pollfd 所属的技术范畴。

参考资料

  1. Ubuntu Manpage: poll [引用日期2026-10-14]
  2. poll.h [引用日期2026-10-14]
  3. linux高性能服务器编程之poll [引用日期2026-10-14]
  4. man pages section 3: Library Interfaces and Headers [引用日期2026-10-14]
  5. poll.2freebsd [引用日期2026-10-14]
  6. Ubuntu Manpage: 名前 [引用日期2026-10-14]
  7. poll [引用日期2026-10-14]
  8. Cross Reference: /fs/select.c [引用日期2026-10-14]
  9. select [引用日期2026-10-14]
  10. poll.h\sys\io - rocket-tools/riscv-gnu-toolchain/glibc.git [引用日期2026-10-14]
  11. revisions [引用日期2026-10-14]
  12. Linux-Kernel Archive: Linux-2.1.23 and administratrivia [引用日期2026-10-14]
  13. Linux的poll与epoll实现(1)---poll [引用日期2026-10-14]
词条评价
词条统计

浏览次数:4 次

阅读量:2 次 · 阅读完成量:0 次

最近更新:2026-10-13T18:55:56Z

历史版本

本条目引用的词条
poll 系统调用 select 系统调用 epoll 文件描述符 I/O 多路复用