在命名管道上执行Poll()函数会持续且立即返回POLLHUP。

12
echo 123 > /tmp/fifo

他们都按照预期从管道中读取(至少我希望这是它们通常应该工作的方式)。

我的问题是,在第一个回显之后,POLLHUP被设置并且它被卡住了,从那时起poll立即返回

如何清除 / 摆脱POLLHUP

它开始让我疯狂:(

是的,管道的另一端已经关闭了(在先前打开后),因此它变成了半关闭状态,但是我的端口仍然是打开和活动的,而且我喜欢它。它没有死亡,我仍然可以通过管道接收新的回声,只是因为poll会哭泣大量的POLLHUP(我甚至没有在事件中请求它们,但是poll可以随便贴上 [man poll:"revents可以包括在事件中指定的任何值,也可以是POLLERR,POLLHUP值之一"]),并且由于那个原因几乎是无用的。

显然,我不能将那个fd从集合中移出,因为我仍然希望在它上面收到新数据的通知。

我不想把它关闭,因为它不是一次性使用的管道,我喜欢重复使用同一个东西而不是扔掉它们……除此之外,我不再拥有管道名称,我只有文件描述符(从fd获取文件名似乎很麻烦……我也谷歌了一下)。

我仍然相信Linux的力量,认为必须有更好的方法来解决这个问题(更高性能/竞争条件安全)。

这里是我读过但没有帮助解决问题的内容。

在我的绝望中,我甚至尝试过这样的事情(这并没有帮助):

    int newfd = dup(fds[i].fd);
    close(fds[i].fd);
    dup2(newfd, fds[i].fd);
    close(newfd);

有什么想法吗?我是不是做错了什么事情?

(我可以随时回到定期尝试读取所有管道的方式(实际上有效),但现在并不关键,但如果很关键,我不知道该怎么办...)

这里有一些代码来复现我的问题(这不是我尝试构建的生产代码,我要轮询的管道显然不止一个...)

#include <stdio.h>

#include <sys/types.h>  // mkfifo
#include <sys/stat.h>   // mkfifo

#include <unistd.h>
#include <fcntl.h>

#include <errno.h>
#include <string.h>

#include <poll.h>


int main(int argc, char **argv) {
    char* pipename = "/tmp/fifo";
    mkfifo(pipename, S_IWUSR | S_IRUSR | S_IRGRP);
    int fd = open(pipename, O_RDONLY | O_NONBLOCK); /// O_ASYNC, O_SYNC

    struct pollfd fds[1];
        fds[0].fd = fd;
        fds[0].events = POLLIN;
        fds[0].revents = 0;

    while (1) {
        int res = poll(fds, 1, 1000);
        if (res<0)  { perror("poll");  return 1; }
        if (res==0) { printf(".\n"); continue; }
        printf("Poll [%d]: 0x%04x on %d\n", 0, fds[0].revents, fds[0].fd);

        char buff[512];
        int count = (int)read(fds[0].fd, buff, (size_t)(sizeof(buff)-1));
        if (count>=0) {
            buff[count] = 0;
            printf("Pipe read %d bytes: '%s'\n", count, buff);
            usleep(1000*100); /// cpu protector sleep for POLLHUP :)
        }   
    }
    return 0;
}

注:

我正在Linux(lubuntu)平台(x64)上使用gcc(4.6.3),但最终我想要交叉编译它以用于嵌入式目标。

我肯定错过了一些信息,所以请问……

解决方案/变通方法:

  1. mark4o建议的变通方法#1是使用O_RDWR而不是O_RDONLY打开管道。这样你就不会一直得到POLLHUP了(当然你也不会得到任何可能会成为问题的东西)。此外,读者需要对管道具有写权限(在某些情况下,你可能没有这个权限)。
1个回答

15

管道提供的仅是单向通道(而不是像套接字一样为每个客户端提供独立的双向通道),因此通常仅在只有一个需要将数据发送到另一个进程的进程时使用。当写入进程关闭管道时,POLLHUP(挂起)会告诉读取进程管道已关闭,可以完成处理并终止。

虽然可以使用具有多个写入进程的管道,但如果消息大于PIPE_BUF或512字节,则需要小心。否则,由多个写入进程同时写入的消息可能会交错。同样,由于只有一个通道,除非您有某些约定(如每个客户端消息以换行符结尾),否则无法确定长消息是来自一个客户端的单个写入还是多个客户端的多个写入。

POLLHUP表示最后一个写入进程已关闭了管道,并持久存在,直到另一个进程打开该管道进行写入或所有读取进程都关闭它。 如果不想这样,可以使用O_RDWR而不是O_RDONLY打开管道,以使其保持打开状态。这可行是因为只要保持打开状态,就始终会有一个写入进程(即您的程序)。


1
感谢您提供的高质量信息。我知道管道原子操作缓冲区限制并不是问题,我也没有计划使用一个管道与多个写入者,而是使用很多单独的管道。使用O_RDWR打开输入管道是可行的,所以我接受了这个答案 :) 感谢您提供的这个想法。 - Steven Spark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接