类似于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的读取端,然后再次将其重新打开,它将可以正常工作。可能没问题,因为我们没有发送大量数据。虽然这不是一个不错的方法或一般的解决方法。
答案 0 :(得分:3)
这是预期的行为,因为输入结束的情况会导致read()
不会阻塞;它立即返回0。
如果您查看man 2 select,它会清楚地表明,如果readfds
上的描述符read()
不会被阻塞(在{{1}时),则会设置一个描述符。 })。
如果您使用poll()
,它也会立即在select()
中以POLLHUP
的形式返回。
如OP所述,正确的解决方法是重新打开FIFO。
由于Linux内核仅维护一个内部管道对象来表示每个打开的FIFO(请参见man 7 fifo和man 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计算机上,这种重新开放也不是任何明智情况的瓶颈。