这两个系统调用的作用,是在指定的文件描述符上监听发生的任何文件事件,以便继续执行后续的文件IO操作。其作用与最古老的文件IO复用接口select差不多,但是poll没有最多1024个文件描述符的限制,因此适用的范围更大。而且,poll接口中使用的数据结构的组织更加直观,操作更加方便。
说到select系统调用的1024个文件描述符的数量限制,完全是历史遗留的产物,因为在select系统调用被创建的那个年代,认为1024已经是个很大的数字了,很少有程序会需要同时用到那么多的文件描述符。但是随着技术的发展,实际的情况已经发生了很大的变化,放在今天的网络世界,这个数字在大多数情况下都已经无法满足实际的需求。所以,新的poll系统调用中,已经破除了这个限制。但是,每个进程能够打开的文件描述符的最大数量,依然受到系统中RLIMIT限制的控制。
这两个系统调用的函数原型分别为:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts, const sigset_t *sigmask);
第二个参数nfds指定fds数组中监听的文件描述符的数量;timeout指定poll调用时的阻塞时间,单位是毫秒,如果指定一个负的超时时间,那么poll就是一直阻塞,直到发生了任何监听中的事件,如果指定为0,那么poll调用会立即返回;如果指定为一个正数,那么该系统调动会在返回之前等待指定的毫秒时间。这里要注意的是,这里实际等待的毫秒数可能与指定的数值并不完全一致,取决于系统定时器的时间分辨率。
第一个参数pollfd结构的定义如下:
struct pollfd {
int fd; /* 标记打开的文件的文件描述符 */
short events; /* 监听的事件列表 */
short revents; /* 实际发生的事件列表 */
};
其中,fd是一个已经打开的文件描述符,也就是指定的要执行事件监听的文件或网络套接字。这里有个很方便的特性是,当fd是负数时,与这个文件相关的事件就会被忽略,所以,当需要在某段时间内暂时跳过对某些文件的监听时,只需要把对应的fd取为对一个的负值,当需要恢复监听的时候,再次取负还原为原来的正数就可以了。
events字段是输入参数,它用位掩码的形式指定对该文件感兴趣的事件。
参数revents是输出参数,当感兴趣的任何事件发生时,内核会把它填充为实际发生的事件,或者填充为几个系统内置的错误标记:POLLERR,POLLHUP或POLLNVAL。
下面列出的是events和revents中能够使用的标志位:
POLLIN - 接收到了可读数据。
POLLPRI - 接收到了高优先级的可读数据,比如,在TCP套接字上的带外数据。
POLLOUT - 文件上可以发送数据了
POLLRDHUP - 流模式的套接字对端关闭了连接,或者关闭了一个半连接。
POLLERR - 发生了错误
POLLHUP - 发生了挂断
POLLNVAL - 请求无效,文件描述符没有打开
这个系统调用如果执行成功,会返回一个表示接收到的事件数量的正整数,如果返回值为0,表示在超时等待的时间内没有收到任何事件。如果发生了任何错误,这个系统调用会返回-1。
常见的错误类型为:
EFAULT - 严重错误,比如当传入的参数地址不是本程序能够访问的有效地址时会引发这个错误。
EINTR - 系统调动执行期间被信号打断。
EINVAL 传入的文件描述符数量超出系统限制。
ENOMEM - 内存不足。
==================== 以下是广告 ====================
更多系统详见的Linux系统编程内容,欢迎订阅GitChat专栏 《攻克Linux系统编程》