poll 函数用于检测一组文件描述符(File Descriptor, fd)上的可读可写和出错事件,其函数签名如下:

#include <poll.h>
 
int poll(struct pollfd* fds, nfds_t nfds, int timeout);

参数解释:

  • fds:指向一个结构体数组的首个元素的指针,每个数组元素都是一个 struct pollfd 结构,用于指定检测某个给定的 fd 的条件;
  • nfds:参数 fds 结构体数组的长度,nfds_t 本质上是 unsigned long int,其定义如下:
    typedef unsigned long int nfds_t;
  • timeout:表示 poll 函数的超时时间,单位为毫秒。

返回值和错误代码

成功时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,poll()返回0;失败时,poll()返回-1,并设置errno为下列值之一:

  • EBADF   一个或多个结构体中指定的文件描述符无效。
  • EFAULTfds   指针指向的地址超出进程的地址空间。
  • EINTR     请求的事件之前产生一个信号,调用可以重新发起。
  • EINVALnfds  参数超出PLIMIT_NOFILE值。
  • ENOMEM   可用内存不足,无法完成请求。

struct pollfd 结构体定义如下:

struct pollfd {
    int   fd;         /* 待检测事件的 fd       */
    short events;     /* 关心的事件组合        */
    short revents;    /* 检测后的得到的事件类型  */
};

struct pollfd 的 events 字段是由开发者来设置,告诉内核我们关注什么事件,而 revents 字段是 poll 函数返回时内核设置的,用以说明该 fd 发生了什么事件。events 和 revents 一般有如下取值:

事件宏事件描述是否可以作为输入(events)是否可以作为输出(revents)
POLLIN数据可读(包括普通数据&优先数据)
POLLOUT数据可写(普通数据&优先数据)
POLLRDNORM等同于 POLLIN
POLLRDBAND优先级带数据可读(一般用于 Linux 系统)
POLLPRI高优先级数据可读,例如 TCP 带外数据
POLLWRNORM等同于 POLLOUT
POLLWRBAND优先级带数据可写
POLLRDHUPTCP连接被对端关闭,或者关闭了写操作,由 GNU 引入
POPPHUP挂起
POLLERR错误
POLLNVAL文件描述符没有打开

poll 检测一组 fd 上的可读可写和出错事件的概念与前文介绍 select 的事件含义一样,这里就不再赘述。poll 与 select 相比具有如下优点:

  • poll 不要求开发者计算最大文件描述符加 1 的大小;
  • 相比于 selectpoll 在处理大数目的文件描述符的时候速度更快;
  • poll 没有最大连接数的限制,原因是它是基于链表来存储的;
  • 在调用 poll 函数时,只需要对参数进行一次设置就好了。

参考资料