IO 多路复用(三)poll 函数

引言

据说 poll 函数起源于 SVR3,最初局限于流设备。而到了 SVR4 取消了这种限制,允许 poll 工作在任何描述符上。 功能与 select 类似,不过在处理流设备时,它能够提供额外的信息。

1.0 函数原型

#include 

int poll(struct pollfd *fdarray, nfds_t nfds, int timeout);

1.1 参数

fdarray

该参数是指向一个结构数组第一个元素的指针。每个数组元素都是一个 pollfd 结构体,用于指定测试某个描述符 fd 的条件。我们看一下它的原型:

struct pollfd 
{
  int   fd;       /* descriptor to check */
  short events;   /* events of interest on fd */
  short revents;  /* events that occurred on fd */
};
  • fd 这个不用多说,就是描述符了。
  • events 就是我们关心的当前描述符的事件。
  • revents 函数把当前描述符的事件状态结果保存在该成员中。

这就理解为,把描述符、关心的事件、事件的状态封装成了一个对象。这些对象组成起来就构成了这个结构数组。它避免了像 select 函数中间三个参数都是值-结果参数。

说明

值-结果参数: 该参数在函数调用的时候作为值传递给函数,函数执行完毕之后将执行结果保存到该参数中,所谓一参两用。值-结果参数一定要传指针。

对于 events 跟 revents 参数,它们每一个都由指定某个特定条件的一位或多位构成,下面是一些常值:

常值 是否作为 events 的输入 是否作为 revents 结果 说明
POLLIN 普通或优先级带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRI 高优先级数据可读
POLLOUT 普通数据可写
POLLWRNORM 普通数据可写
POLLWRBAND 优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述符不是一个打开的文件

我们需要记住 poll 识别三类数据:普通、优先级带、高优先级。

补充:

POLLIN 可被定义为 POLLRDNORM 和 POLLRDBAND 的逻辑或;POLLOUT 等同于 POLLWRNORM。

还要注意的是,如果是 TCP 和 UDP 套接字,一下的条件会引起 poll 返回特定的 revent (然而不性的是, POSIX 在其 poll 的定义中留了许多空洞,会造成多种方式可返回相同的条件)。

  • 所有正规 TCP 数据和所有 UDP 数据都被认为是普通数据。
  • TCP 的带外数据被认为是优先级带数据。
  • 当 TCP 连接的读半部分关闭时(譬如收到了一个来自对端的 FIN),也被认为是普通数据,随后的读操作将返回 0。
  • TCP 连接存在错误既可认为是普通数据,也可认为是错误(POLLERR)。无论哪种情况,随后的读操作将返回 -1,并把 errno 设置成合适的值。这可用于处理诸如接收到 RST 或发生超时等条件。
  • 在监听套接字上有新的连接可用既可认为是普通数据,也可认为是优先级数据。大多数实现视为普通数据。
  • 非阻塞式 connect 的完成被认为是使相应套接字可写。

nfds

该参数指定了结构数组(也就是第一个参数)中元素的个数。该参数被声明为:nfds_t 类型,对应的其实是:unsigned int。

timeout

该参数指定了 poll 函数返回前等待多长时间。

  • INFTIM
    永久等待。
  • 0
    立刻返回,不阻塞进程。
  • n
    指定等待毫秒数 n。

INFTIM 常值被定义为一个负值。如果系统不能提供毫秒级精度的定时器,该值就向上舍入到最接近的支持值。

补充:

POSIX 规范要求在头文件 中定义 INFTIM,不过许多系统仍然把它定义在头文件 中。

1.2 返回值

  • 0
    运行超时。
  • -1
    函数执行发生错误。
  • n
    已经就绪的描述符数量,即成员 revents 值非 0 的描述符数量。

如果不想关心某个描述符,只需要把该描述符对应的 pollfd 结构的 fd 成员设置成一个负值就可以了。poll 函数将会忽略这样的 pollfd 结构的 events 成员,并且返回时,将它的 revents 成员值置为 0。

2.0 注意

对于 poll 函数,就不再需要关心每个进程中最大描述符的数量之类的问题了,因为 poll 函数采用 pollfd 结构体数组的方式传递给内核,并且把传递数组元素的数量给内核的责任交给了调用者。所以,内核也就不在需要知道类似 fd_set 的固定大小的数据类型了。


参考

  • Unix 网络编程 卷一

你可能感兴趣的:(IO 多路复用(三)poll 函数)