命名管道上的Poll()会不断地立即返回POLLHUP

Ste*_*ark 6 c linux named-pipes

打开一个带有非阻塞标志的命名管道(由mkfifo创建的fifo)(打开(... O_NONBLOCK))然后开始轮询(poll(...)).到现在为止还挺好.然后从命令行我做了几个

echo 123 > /tmp/fifo
Run Code Online (Sandbox Code Playgroud)

它们都按预期读出管道(至少我希望它们应该如何正常工作).

我的问题是,在第一次回显之后,POLLHUP被设置并且它被卡住,轮询立即从该点返回.

如何清除 /摆脱POLLHUP

它开始让我发疯:(

是的,管道的另一端被关闭(之前打开),所以它变成了一半关闭,但我的结束仍然是开放和活着的,我喜欢这样.它还没死,我仍然可以通过管道收到新的回声,它只是民意调查POLLHUP的河流(我在事件中首先没有要求,但民意调查只能标记它们[人民调查:" revents可以包括在事件中指定的任何一个,或者值POLLERR,POLLHUP "] 之一,并且因此而无用.

显然我无法将fd从集合中删除,因为我仍然希望收到关于它的新数据的通知.

我不想关闭它,因为它不是一次性使用管道,我喜欢重复使用同样的东西而不是把它们扔掉......除此之外我还没有管道名称,我只有文件描述符(从fd获取文件名看起来像个婊子......我也用Google搜索...)

我仍然相信Linux的强大功能,并且必须有更好的(更高性能/竞争条件安全)方式来实现这一点.

这是我读过的,但没有帮助解决问题.

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

    int newfd = dup(fds[i].fd);
    close(fds[i].fd);
    dup2(newfd, fds[i].fd);
    close(newfd);
Run Code Online (Sandbox Code Playgroud)

有任何想法吗?我做错了什么吗?

(我总是可以回到试图定期读取所有管道(实际上是有效的),现在这不是延迟关键,但我不知道如果它是......我会怎么做?)

下面是一些代码重现我的问题(这不是我想要建立的生产代码,有明显超过1管,我想轮询...)

#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;
}
Run Code Online (Sandbox Code Playgroud)

笔记:

我在Linux(lubuntu)平台(x64)上使用gcc(4.6.3).但最后我想为嵌入式目标交叉编译它.

我敢肯定我一定错过了一些和平信息,所以请你离开......

解决方案/解决方法:

  1. mark4o建议的解决方法#1是用O_RDWR而不是打开管道O_RDONLY.这样你就不会得到常数POLLHUP-s(当然你不会得到任何女巫可能是个问题).此外,读者还需要对管道的写入权限(在某些情况下您可能没有).

mar*_*k4o 6

因为管道仅提供单个单向信道(未为每个客户端等的插座的独立双向信道)中,当你只有一个的过程,需要将数据发送到仅一个其它过程它们被正常使用.当POLLHUP编写器关闭管道时,(挂断)告诉读者管道已关闭,它可以完成处理并终止.

可以使用具有多个写入器的管道,但如果消息可能大于PIPE_BUF或等于512字节,则需要小心.否则,因为它只是一个通道,所以同时写入多个写入器的消息可以交错.此外,因为它是单个通道,您将无法判断长消息是来自一个客户端的单个写入还是来自多个客户端的多个写入,除非您有一些约定,例如每个客户端消息一行(由换行符终止) .

POLLHUP表示最后一个编写器已关闭管道,并持续到另一个进程打开管道进行写入或所有读取器关闭.如果您不想这样做,请打开管道O_RDWR而不是O_RDONLY使管道保持打开状态.这是有效的,因为只要你打开它就会有一个作家(你的程序).