Linux命名为fifo non-blocking read select返回伪造的read_fds [英] Linux named fifo non-blocking read select returns bogus read_fds

查看:89
本文介绍了Linux命名为fifo non-blocking read select返回伪造的read_fds的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

类似于不久前在内核3上问到的问题.x ,但我在4.9.37上看到了它. 用mkfifo -m 0666创建命名的fifo.在读取方面,用

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);

结果fd被传递给对select()的调用.一切正常,直到我运行echo >> <fifo-name>.

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

现在,select()返回后,fd出现在read_fds中. fd上的read()将返回一个字节的数据.到目前为止,一切都很好.

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.

下次调用select()并返回时,fd仍出现在read_fds中,但是read()将始终返回零,表示没有数据.实际上,读取端将消耗100%的处理器容量.这与所参考问题所观察到的问题完全相同.

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?

我发现如果我关闭了fifo的读取端,然后再次将其重新打开,它将可以正常工作.这可能是可以的,因为我们没有发送大量数据.虽然这不是一个不错的方法或一般的解决方法.

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.

推荐答案

这是预期的行为,因为输入结束的情况导致read()不会阻塞;它立即返回0.

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

如果您查看 man 2 select ,清楚地说,如果readfds中的描述符不被阻塞(在select()调用时),则会在readfds中设置一个描述符.

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).

如果您使用了 poll() ,会立即以revents中的POLLHUP返回.

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

如OP所述,正确的解决方法是重新打开FIFO.

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

因为Linux内核仅维护一个内部管道对象来表示每个打开的FIFO(请参阅 man 7 pipe ),在Linux中,健壮的方法是在遇到输入结束时(read()返回0)打开另一个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.

在伪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 */

Linux中的文件描述符分配策略是tempfd将是编号最小的空闲描述符.

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

在我的系统(Core i5-7200U笔记本电脑)上,以这种方式重新打开FIFO不到1.5 µs.也就是说,每秒可以完成约680,000次.我认为,即使在低功率的嵌入式Linux计算机上,这种重新开放也不是任何明智情况的瓶颈.

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.

这篇关于Linux命名为fifo non-blocking read select返回伪造的read_fds的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆