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;
}
posted @ 2021-06-20 21:59  suntus  阅读(97)  评论(0编辑  收藏  举报