Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
582 views
in Technique[技术] by (71.8m points)

c - Poll() on Named Pipe returns with POLLHUP constantly and immediately

I open a named pipe (fifo created by mkfifo) with non blocking flag (open(...O_NONBLOCK)) then start polling (poll(...)). So far so good. Then from the command line I do a couple of

echo 123 > /tmp/fifo

They are all read out of the pipe as expected (at least I expect that's how they should work normally).

My problem is that after the first echo, POLLHUP is set and it is stuck, poll returns immediately from that point.

How do I clear / get rid of POLLHUP ?

It starts to drive me crazy :(

Yes the other end of the pipe got closed (after it was opened previously), so it became half closed, but my end is still open and alive and I like it that way. It's not dead, I can still receive new echoes via the pipe, it's just poll cries a river of POLLHUP (which I didn't even requested in the first place in events, but poll can just tag them on anyway [man poll: "revents can include any of those specified in events, or one of the values POLLERR, POLLHUP"]) and pretty much useless because of that.

Obviously I cannot get that fd out of the set, because I still want to be notified about new data on it.

I do NOT want to close it, because it's not a use-once-pipe, I like reusing the same thing and not throw them away... among other things I do not have the pipe name anymore, I only have the file descriptor (and getting the filename from fd seems like a bitch... I Googled that too...)

I still believe in the power of Linux, and that there must be a better (more performance efficient / race condition safe) way to do this.

Here is what I have read, but did not help solving the problem.

In my desperation I tried doing even things like this (which did not help):

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

Any ideas? Am I doing something completely wrong?

(I can always go back to just trying to read all pipes periodically (which actually works), this right now is not latency critical, but I don't know what would I do if it were...)

Here are some code to reproduce my problem (This is not the production code that I'm trying to build, there are obviously more than 1 pipe that I want to poll...)

#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(".
"); continue; }
        printf("Poll [%d]: 0x%04x on %d
", 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'
", count, buff);
            usleep(1000*100); /// cpu protector sleep for POLLHUP :)
        }   
    }
    return 0;
}

Notes:

I'm using gcc (4.6.3) on Linux (lubuntu) platform (x64). But in the end I want to cross compile it for embedded target.

I'm sure I must have missed some information peaces out, so ask away...

Solution/Workaround:

  1. Workaround #1 suggested by mark4o is to open the pipe with O_RDWR instead of O_RDONLY. This way you won't get constant POLLHUP-s (of course you won't get any witch might be a problem). Also the reader will need write permission to the pipe (which you might not have in certain cases).
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Because pipes provide only a single unidirectional channel (not a separate bidirectional channel for each client like a socket), they are normally used when you have just one process that needs to send data to only one other process. When the writer closes the pipe, POLLHUP (hangup) tells the reader that the pipe is closed and it can finish processing and terminate.

It is possible to use a pipe with multiple writers but you will need to be careful if the messages could be larger than PIPE_BUF or 512 bytes. Otherwise, because it is only a single channel, the messages from multiple writers writing at the same time could be interleaved. Also because it is a single channel you won't be able to tell whether a long message is a single write from one client or multiple writes from multiple clients, unless you have some convention like one line (terminated by a newline) per client message.

POLLHUP indicates that the last writer has closed the pipe, and persists until another process opens the pipe for writing or it is closed by all readers. If you don't want this, open the pipe with O_RDWR instead of O_RDONLY so that the pipe will stay open. This works because then there will always be a writer (your program) as long as you have it open.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...