poll
函数类似于select
,但是程序员接口有所不同。虽然poll
函数是System V引入进来支持STREAMS子系统的,但是poll
函数可用于任何类型的文件描述符。
#include <poll.h>
int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);
//返回值:准备就绪的描述符数目;若超时,返回0;若出错,返回-1
与select
不同,poll
不是为每个条件(可读性、可写性和异常条件)构造一个描述符集,而是构造一个pollfd
结构的数组,每个数组元素指定一个描述符编号以及我们对该描述符感兴趣的条件。
struct pollfd {
int fd; /* file descriptor to check, or < 0 toignore */
short events; /* events of interest on fd */
short revents; /* events that occurred on fd */
};
fdarray
数组中的元素数由nfds
指定。
由于历史原因,在如何声明 nfds 参数方面有几种不同的方式。SVR3 将 nfds 的类型指定为unsigned long,这似乎是太大了。在SVR4手册[AT&T 1990d]中,poll原型的第二个参数的数据类型为size_t(见图2-21中的基本系统数据类型)。但在<poll.h>包含的实际原型中,第二个参数的数据类型仍指定为unsigned long。Single UNIX Specification定义了新类型nfds_t,该类型允许实现选择对其合适的类型并且隐藏了应用细节。注意,因为返回值表示数组中满足事件的项数,所以这种类型必须大得足以保存一个整数。
对应于 SVR4 的 SVID[AT&T 1989]上显示,poll 的第一个参数是 struct pollfd fdarray[],而SVR4手册页[AT&T 1990d]上则显示该参数为struct pollfd*fdarray。在C语言中,这两种声明是等价的。我们使用第一种声明是为了重申fdarray指向的是一个结构数组,而不是指向单个结构的指针。
应将每个数组元素的events
成员设置为图14-17中所示值的一个或几个,通过这些值告诉内核我们关心的是每个描述符的哪些事件。返回时,revents
成员由内核设置,用于说明每个描述符发生了哪些事件。(注意,poll
没有更改events
成员。这与select
不同,select
修改其参数以指示哪一个描述符已准备好了。)
图14-17中的前4行测试的是可读性,接下来的3行测试的是可写性,最后3行测试的是异常条件。最后3行是由内核在返回时设置的。即使在events
字段中没有指定这3个值,如果相应条件发生,在revents
中也会返回它们。
有些poll事件的名字中包含BAND,它指的是STREAMS当中的优先级波段。想要了解关于STREAMS和优先级波段的更多信息,可以查看Rago[1993]。
图14-17 poll的events和revents标志
当一个描述符被挂断(POLLHUP)后,就不能再写该描述符,但是有可能仍然可以从该描述符读取到数据。
poll
的最后一个参数指定的是我们愿意等待多长时间。如同select
一样,有3种不同的情形。
timeout == -1
永远等待。(某些系统在<stropts.h>
中定义了常量INFTIM
,其值通常是-1
。)当所指定的描述符中的一个已准备好,或捕捉到一个信号时返回。如果捕捉到一个信号,则poll
返回-1
,errno
设置为EINTR
。
timeout == 0
不等待。测试所有描述符并立即返回。这是一种轮询系统的方法,可以找到多个描述符的状态而不阻塞poll
函数。
timeout > 0
等待timeout
毫秒。当指定的描述符之一已准备好,或timeout
到期时立即返回。如果timeout
到期时还没有一个描述符准备好,则返回值是0。(如果系统不提供毫秒级精度,则timeout
值取整到最近的支持值。)
理解文件尾端与挂断之间的区别是很重要的。如果我们正从终端输入数据,并键入文件结束符,那么就会打开POLLIN
,于是我们就可以读文件结束指示(read
返回0)。revents
中的POLLHUP
没有打开。如果正在读调制解调器,并且电话线已挂断,我们将接到POLLHUP
通知。
与select
一样,一个描述符是否阻塞不会影响poll
是否阻塞。
select和poll的可中断性
中断的系统调用的自动重启是由4.2BSD引入的(见10.5节),但当时select
函数是不重启的。这种特性在大多数系统中一直延续了下来,即使指定了SA_RESTART
选项也是如此。但是,在SVR4上,如果指定了SA_RESTART
,那么select
和poll
也是自动重启的。为了在将软件移植到SVR4派生的系统上时阻止这一点,如果信号有可能会中断select
或poll
,就要使用signal_intr
函数(见图10-19)。
本书说明的各种实现在接到一信号时都不重启动poll和select,即便使用了SA_RESTART标志也是如此。