poll相关
IO多路复用。
1. 定义
#include <poll.h>
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <poll.h>
struct timespec {
long tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
int ppoll(struct pollfd *fds, nfds_t nfds,
const struct timespec *timeout_ts, const sigset_t *sigmask);
2. 基本使用
跟select(2)
类似,等待一组文件描述符上的事件。
如果 fd
是负数,poll(2)
会忽略 events
,并且返回的revents
是0,可以在单次poll(2)
调用中,忽略某一个fd
events
是入参,输入对fd
上感兴趣的事件,是个bitmask,0表示对所有事件都不感兴趣。
revents
是出参,表示哪些事件发生了,也是个bitmask,可能会比 events
多出来三个值 POLLER, POLLHUP, POLLNVAL
。
timeout
是ms,表示poll(2)
最少会阻塞的时间,0表示非阻塞,立刻返回,即使没有事件发生也会返回;负数表示一直阻塞,直到有事件发生。
bitmask可以有的值为:
- POLLIN: 有数据可以读
- POLLPRI: 紧急数据可读(TCP上的带外数据等)
- POLLOUT: 数据可写,这时候进行写操作不会阻塞
- POLLRDHUP(>=2.6.17): 流式socket的对端断开连接或者关闭了写操作。比如有
_GNU_SOURCE
宏才能使用该定义 - POLLERR(out): 有错误发生
- POLLHUP(out): 挂起。写端关闭,在读端
poll()
时,会出现该错误。 - POLLNVAL(out): 非法请求,fd未打开。等价于EBADF。
ppoll(2)
允许应用安全的等待fd上事件发生,或者捕获了信号。类似 select(2)
和 pselect(2)
的关系。
这样的调用:
ready = ppoll(&fds, nfds, timeout_ts, &sigmask);
等价于 原子性 的执行:
sigset_t origmask;
int timeout;
timeout = (timeout_ts == NULL) ? -1 :
(timeout_ts.tv_sec * 1000 + timeout_ts.tv_nsec / 1000000);
sigprocmask(SIG_SETMASK, &sigmask, &origmask);
ready = poll(&fds, nfds, timeout);
sigprocmask(SIG_SETMASK, &origmask, NULL);
3. 返回值
大于0: 成功,有事件发生的fd的个数(这些fd上的 revents 非0)。
0: 超时,没有事件发生
-1: 失败,需要检查对应的errno。
errno:
- EFAULT: 参数数组地址无效
- EINTR: 有信号发生
- EINVAL: nfds 值超出了
RLIMIT_NOFILE
的值 - ENOMEM: 没有内存来申请fd表
4. 其他
有些实现定义了 timeout 参数无穷大值 INFTIM
,值为-1, glibc 没有定义。
linux提供的 ppoll(2)
系统调用接口会更新 timeout_ts
参数,glibc封装的时候不会更新,需要注意
5. 关于POLLHUP
这里稍详细的解释了POLLHUP: https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html
POLLHUP
A device has been disconnected, or a pipe or FIFO has been closed by the last process that had it open for writing. Once set, the hangup state of a FIFO shall persist until some process opens the FIFO for writing or until all read-only file descriptors for the FIFO are closed. This event and POLLOUT are mutually-exclusive; a stream can never be writable if a hangup has occurred. However, this event and POLLIN, POLLRDNORM, POLLRDBAND, or POLLPRI are not mutually-exclusive. This flag is only valid in the revents bitmask; it shall be ignored in the events member.
POLLNVAL
也就是写端关闭,在读端进行poll()
时,会报出这个错,然后在写端重新打开后,该fd还会正常,有人提供了个示例,演示了如何触发 POLLHUP
// 打开pipe, 关闭写端,在读端进行poll()
#include <unistd.h>
#include <stdio.h>
#include <poll.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(void)
{
int p[2];
struct pollfd ufd;
if (pipe(p) < 0) {
perror("pipe");
return EXIT_FAILURE;
}
if (close(p[1]) < 0) { /* close the write fd */
perror("close");
return EXIT_FAILURE;
}
memset(&ufd, 0, sizeof ufd);
ufd.fd = p[0]; /* poll the read fd after the write fd is closed */
ufd.events = POLLIN;
if (poll(&ufd, 1, 1000) < 0) {
perror("poll");
return EXIT_FAILURE;
}
switch(ufd.revents & (POLLIN|POLLHUP)) {
case POLLIN: printf("POLLIN\n"); break;
case POLLHUP: printf("POLLHUP\n"); break;
case POLLIN|POLLHUP: printf("POLLIN|POLLHUP\n"); break;
case POLLERR: printf("POLLERR\n"); break;
default: printf("%#x\n", (unsigned)ufd.revents); break;
}
return EXIT_SUCCESS;
}