IO多路复用。
1. 定义
1 |
|
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)
的关系。
这样的调用:
1 | ready = ppoll(&fds, nfds, timeout_ts, &sigmask); |
等价于 原子性 的执行:
1 | sigset_t origmask; |
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
1 | // 打开pipe, 关闭写端,在读端进行poll() |