Linux named fifo non-blocking read select returns bogus read_fds

限于喜欢 提交于 2020-01-06 09:09:07

问题


Similar to the problem asked a while ago on kernel 3.x, but I'm seeing it on 4.9.37. The named fifo is created with mkfifo -m 0666. On the read side it is opened with

int fd = open(FIFO_NAME, O_RDONLY | O_NONBLOCK);

The resulting fd is passed into a call to select(). Everything works ok, till I run echo >> <fifo-name>.

Now the fd appears in the read_fds after the select() returns. A read() on the fd will return one byte of data. So far so good.

The next time when select() is called and it returns, the fd still appears in the read_fds, but read() will always return zero meaning with no data. Effectively the read side would consume 100% of the processor capacity. This is exactly the same problem as observed by the referenced question.

Has anybody seen the same issue? And how can it be resolved or worked-around properly?

I've figured out if I close the read end of the fifo, and re-open it again, it will work properly. This probably is ok because we are not sending a lot of data. Though this is not a nice or general work-around.


回答1:


This is expected behaviour, because the end-of-input case causes a read() to not block; it returns 0 immediately.

If you look at man 2 select, it says clearly that a descriptor in readfds is set if a read() on that descriptor would not block (at the time of the select() call).

If you used poll(), it too would immediately return with POLLHUP in revents.


As OP notes, the correct workaround is to reopen the FIFO.

Because the Linux kernel maintains exactly one internal pipe object to represent each open FIFO (see man 7 fifo and man 7 pipe), the robust approach in Linux is to open another descriptor to the FIFO whenever an end of input is encountered (read() returning 0), and close the original. During the time when both descriptors are open, they refer to the same kernel pipe object, so there is no race window or risk of data loss.

In pseudo-C:

fifoflags = O_RDONLY | O_NONBLOCK;
fifofd = open(fifoname, fifoflags);
if (fifofd == -1) {
    /* Error checking */
}

/* ... */

/* select() readfds contains fifofd, or
   poll() returns POLLIN for fifofd: */

    n = read(fifofd, buffer, sizeof buffer)
    if (!n) {
        int tempfd;

        tempfd = open(fifopath, fifoflags);
        if (tempfd == -1) {
            const int cause = errno;
            close(fifofd);

            /* Error handling */

        }
        close(fifofd);
        fifofd = tempfd;

        /* A writer has closed the FIFO. */

    } else
        /* Handling for the other read() result cases */

The file descriptor allocation policy in Linux is such that tempfd will be the lowest-numbered free descriptor.

On my system (Core i5-7200U laptop), reopening a FIFO in this way takes less than 1.5 µs. That is, it can be done about 680,000 times a second. I do not think this reopening is a bottleneck for any sensible scenario, even on low-powered embedded Linux machines.



来源:https://stackoverflow.com/questions/52015639/linux-named-fifo-non-blocking-read-select-returns-bogus-read-fds

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!