【问题标题】:POLLHUP vs POLLNVAL, or what is POLLHUP? [duplicate]POLLHUP vs POLLNVAL,或者什么是 POLLHUP? [复制]
【发布时间】:2014-09-28 14:37:11
【问题描述】:

手册页说 poll(2):

POLLHUP - 挂断(仅输出)

POLLNVAL - 无效请求:fd 未打开(仅输出)

到底有什么区别?编写一个简单的程序表明,如果我关闭文件描述符,POLLNVAL 将触发,然后尝试从关闭的 fd 中读取。但是,我无法找到返回 POLLHUP 的方法。

【问题讨论】:

  • 这是我的man 页面所说的:POLLHUP The device or socket has been disconnected. This flag is output only, and ignored if present in the input events bitmask. Note that POLLHUP and POLLOUT are mutually exclusive and should never be present in the revents bitmask at the same time.

标签: c unix pipe poll-syscall


【解决方案1】:

POLLNVAL 等价于EBADF:这意味着文件描述符实际上并不引用任何打开的文件,即它是closed 或从未打开过。除非由于编程错误或故意尝试查询文件描述符是否无效,否则这永远不会发生。外部条件,例如对等端关闭其端网络套接字或管道,永远不能关闭你的文件描述符你的端套接字或管道。如果可以的话,这将导致基本上任何使用套接字/管道/等的程序都存在大量漏洞。

另一方面,POLLHUP 表示您的文件描述符是有效的,但它处于以下状态:

设备已断开连接,或者管道或 FIFO 已被打开以进行写入的最后一个进程关闭。一旦设置,FIFO 的挂起状态将持续到某个进程打开 FIFO 进行写入或直到 FIFO 的所有只读文件描述符关闭。此事件和 POLLOUT 互斥;如果发生挂断,则流永远不可写。但是,此事件与 POLLIN、POLLRDNORM、POLLRDBAND 或 POLLPRI 不是互斥的。此标志仅在 revents 位掩码中有效;在 events 成员中应忽略它。

来源:http://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html

如果你想看POLLHUP,只需打开一个pipe,关闭读取端,用poll查询写入端即可。

【讨论】:

  • 在(至少)Linux 上,关闭管道的读取端并轮询写入端会得到POLLERR,而不是POLLHUP。我认为原因是尝试写入会导致错误(EPIPE,如果忽略 SIGPIPE)。
  • 也许我应该切换读/写?
  • 我看到 POLLHUP 的读写在这个意义上是“切换的”。我打开一个 FIFO 进行读取,然后进行轮询;这会优雅地阻塞,直到其他东西写入 FIFO。然后我得到 POLLIN 并读取数据。写入器关闭它的结尾,我再次轮询,打算等待另一个写入器打开 FIFO 并写入它——但我立即收到一个 POLLHUP。 (或者一个 POLLHUP | POLLIN,如果我反复轮询直到写入发生。)我想我需要在再次轮询之前刷新我的文件描述符,以避免忙等待。
【解决方案2】:

如果您的目标是编写触发POLLHUP 的程序,请尝试打开管道,关闭其写入端,然后poll() 读取端(代码修改自http://www.greenend.org.uk/rjk/tech/poll.html):

#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;
}

以上为我打印POLLHUP

【讨论】:

  • 非常有用的例子,谢谢!
  • switch 语句中的掩码意味着永远不会采用POLLERRPOLLHUP 的情况。
  • @Pod 你是对的。我不知道我在写上面的代码时在想什么:(。
  • 其实我觉得需要在switch语句中把掩码改成(POLLIN|POLLHUP|POLLERR)
  • 那也行不通,好像设置了多个位,那么很多情况都会失败。选项是 1) 枚举所有案例,或者 2) 只检查每个相关位的 3 个 ifs
【解决方案3】:

POLLNVAL 表示文件描述符值无效。它通常表示您的程序中存在错误,但如果您关闭了文件描述符并且此后没有打开任何可能重用该描述符的文件,您可以依靠 poll 返回 POLLNVAL

POLLHUP 基本上意味着连接另一端的东西已经关闭了它的连接端。 POSIX 将其描述为

设备已断开连接。此事件和 POLLOUT 互斥;如果发生挂断,则流永远不可写。

这对于终端来说已经很清楚了:终端已经消失(生成 SIGHUP 的同一事件:调​​制解调器会话已终止,终端仿真器窗口已关闭等)。 POLLHUP 永远不会发送给常规文件。对于管道和套接字,it depends。 Linux 在管道写入端的程序关闭管道时设置POLLHUP,并在套接字的另一端关闭套接字时设置POLLIN|POLLHUP,但POLLIN 仅用于套接字关闭。最近的 *BSD 设置 POLLIN|POLLUP 当管道的写入端关闭管道时,套接字的行为更加多变。

要观察POLLHUP,请从终端读取程序并关闭终端。或者,在 Linux 上,让您的程序从管道读取并关闭写入端(例如 sleep 1 | yourprogram)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-12
    • 1970-01-01
    • 2011-09-30
    • 2021-11-03
    • 1970-01-01
    • 2012-04-01
    • 2017-10-02
    • 2018-11-29
    相关资源
    最近更新 更多