简体   繁体   English

Linux命名为fifo non-blocking read select返回假read_fds

[英]Linux named fifo non-blocking read select returns bogus read_fds

Similar to the problem asked a while ago on kernel 3.x , but I'm seeing it on 4.9.37. 类似于不久前在内核3.x上问问题 ,但我在4.9.37上看到了。 The named fifo is created with mkfifo -m 0666 . 使用mkfifo -m 0666创建命名的fifo。 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() . 结果fd传递给对select()的调用。 Everything works ok, till I run echo >> <fifo-name> . 一切正常,直到我运行echo >> <fifo-name>

Now the fd appears in the read_fds after the select() returns. 现在,在select()返回之后, fd出现在read_fds A read() on the fd will return one byte of data. fd上的read()将返回一个字节的数据。 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. 下次调用select()并返回时, fd仍出现在read_fds ,但read()将始终返回零,意味着没有数据。 Effectively the read side would consume 100% of the processor capacity. 实际上,读取端将消耗100%的处理器容量。 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. 我已经弄清楚了,如果我关闭了fifo的读取端,然后再次将其重新打开,它将可以正常工作。 This probably is ok because we are not sending a lot of data. 可能没问题,因为我们没有发送大量数据。 Though this is not a nice or general work-around. 虽然这不是一个不错的方法或一般的解决方法。

This is expected behaviour, because the end-of-input case causes a read() to not block; 这是预期的行为,因为输入结束的情况导致read()不会阻塞; it returns 0 immediately. 它立即返回0。

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). 如果您看一下man 2 select ,它将清楚地表明,如果该描述符上的read()不会阻塞(在select()调用时),则会在readfds中设置一个描述符。

If you used poll() , it too would immediately return with POLLHUP in revents . 如果您使用poll()它也将立即返回与POLLHUPrevents


As OP notes, the correct workaround is to reopen the FIFO. 如OP所述,正确的解决方法是重新打开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. 由于Linux内核仅维护一个内部管道对象来表示每个打开的FIFO(请参阅man 7 fifoman 7管道 ),因此Linux中的健壮方法是在遇到输入结束时向FIFO打开另一个描述符( read()返回0),然后关闭原始文件。 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: 在伪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. Linux中的文件描述符分配策略是tempfd将是编号最小的空闲描述符。

On my system (Core i5-7200U laptop), reopening a FIFO in this way takes less than 1.5 µs. 在我的系统(Core i5-7200U笔记本电脑)上,以这种方式重新打开FIFO不到1.5 µs。 That is, it can be done about 680,000 times a second. 也就是说,每秒可以完成约680,000次。 I do not think this reopening is a bottleneck for any sensible scenario, even on low-powered embedded Linux machines. 我认为,即使在低功率的嵌入式Linux计算机上,这种重新开放也不是任何明智情况的瓶颈。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM