引言
据说 poll 函数起源于 SVR3,最初局限于流设备。而到了 SVR4 取消了这种限制,允许 poll 工作在任何描述符上。 功能与 select 类似,不过在处理流设备时,它能够提供额外的信息。
1.0 函数原型
#include <sys/poll.h>
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 规范要求在头文件 <poll.h> 中定义 INFTIM,不过许多系统仍然把它定义在头文件 <sys/stropts.h> 中。
1.2 返回值
-
0
运行超时。 -
-1
函数执行发生错误。 -
n
已经就绪的描述符数量,即成员 revents 值非 0 的描述符数量。
如果不想关心某个描述符,只需要把该描述符对应的 pollfd 结构的 fd 成员设置成一个负值就可以了。poll 函数将会忽略这样的 pollfd 结构的 events 成员,并且返回时,将它的 revents 成员值置为 0。
2.0 注意
对于 poll 函数,就不再需要关心每个进程中最大描述符的数量之类的问题了,因为 poll 函数采用 pollfd 结构体数组的方式传递给内核,并且把传递数组元素的数量给内核的责任交给了调用者。所以,内核也就不在需要知道类似 fd_set 的固定大小的数据类型了。
网友评论