名为fifo非阻塞读取选择的Linux返回假read_fds

时间:2018-08-25 09:03:29

标签: c linux select named-pipes nonblocking

类似于problem asked a while ago on kernel 3.x,但我在4.9.37上看到了它。 命名的fifo是使用mkfifo -m 0666创建的。在读取方面,用

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

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

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

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

有人看到过同样的问题吗?以及如何解决或正确解决呢?

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

1 个答案:

答案 0 :(得分:3)

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

如果您查看man 2 select,它会清楚地表明,如果readfds上的描述符read()不会被阻塞(在{{1}时),则会设置一个描述符。 })。

如果您使用poll(),它也会立即在select()中以POLLHUP的形式返回。


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

由于Linux内核仅维护一个内部管道对象来表示每个打开的FIFO(请参见man 7 fifoman 7 pipe),因此Linux中的健壮方法是,每当结束时打开另一个FIFO描述符。遇到输入(revents返回0),然后关闭原始文件。在同时打开两个 描述符时,它们引用相同的内核管道对象,因此不会出现竞争窗口或数据丢失的风险。

在伪C中:

read()

Linux中的文件描述符分配策略是使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 */ 是编号最小的空闲描述符。

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