phi*_*294 8 pipe tail cat fifo
我使用了一个文件描述符
mkfifo fifo
Run Code Online (Sandbox Code Playgroud)
一旦有东西写入这个管道,我想立即重用它。我应该使用
tail -f fifo
Run Code Online (Sandbox Code Playgroud)
或者
while true; do cat fifo; done
Run Code Online (Sandbox Code Playgroud)
?
他们似乎做同样的事情,我无法衡量性能差异。但是,当系统不支持inotify(例如Busybox)时,前者需要
tail -f -s 0 fifo
Run Code Online (Sandbox Code Playgroud)
但这会以 100% 的使用率消耗 CPU(测试一下:mkfifo fifo && busybox tail -f -s 0 fifo & echo hi>fifo
/ 用fg 1
和取消CtrlC)。那么 while-true-cat 是更可靠的解决方案吗?
Sté*_*las 12
当你这样做时:
cat fifo
Run Code Online (Sandbox Code Playgroud)
假设还没有其他进程打开fifo
写入,cat
将阻塞open()
系统调用。当另一个进程打开文件进行写入时,管道将被实例化并open()
返回。cat
将read()
在循环中调用并read()
阻塞,直到其他进程将数据写入管道。
cat
当所有其他写入进程将其文件描述符关闭到fifo
. 在这些点cat
终止并且管道被破坏¹。
您需要cat
再次运行以读取之后将写入的内容fifo
(但通过不同的管道实例)。
在:
tail -f file
Run Code Online (Sandbox Code Playgroud)
就像cat
,tail
将等待一个进程打开一个文件进行写入。但是在这里,由于您没有-n +1
从头指定要复制,tail
因此需要等到 eof 才能找出最后 10 行是什么,因此在写入结束关闭之前您将看不到任何内容。
在此之后,tail
将不会关闭它的fd到,这意味着管道实例不会被破坏,并且仍将尝试从管道每秒(在Linux上阅读管道,轮询可以通过使用避免inotify
和一些版本GNUtail
在那里这样做)。这read()
将返回 eof (马上,这就是为什么你看到 100% CPU with -s 0
(这与 GNUtail
意味着不在read()
s之间等待而不是等待一秒钟)),直到其他进程再次打开文件进行写入。
在这里,您可能想要使用cat
,但要确保管道实例在实例化后始终存在。为此,在大多数系统上,您可以执行以下操作:
cat 0<> fifo # the 0 is needed for recent versions of ksh93 where the
# default fd changed from 0 to 1 for the <> operator
Run Code Online (Sandbox Code Playgroud)
cat
的 stdin 将为读取和写入打开,这意味着cat
永远不会在其上看到 eof(即使没有其他进程打开fifo
写入,它也会立即实例化管道)。
在不起作用的系统上,您可以改为:
cat < fifo 3> fifo
Run Code Online (Sandbox Code Playgroud)
这样,只要其他一些进程打开fifo
写入,第一个只读open()
将返回,此时 shell 将open()
在启动之前执行只写cat
,这将防止管道再次被破坏。
所以,总结一下:
cat file
,在第一轮之后就不会停止。tail -n +1 -f file
:read()
在第一轮之后它不会每秒都做一次无用的事情,管道的一个实例上永远不会有 eof,当第二个进程打开管道进行写入时,不会有长达一秒的延迟第一个已经关闭它。tail -f file
。除了上述之外,它不必等待第一轮结束才能输出一些东西(只有最后 10 行)。cat file
循环相比,只有一个管道实例。¹中提到的比赛窗口将被避免。¹此时,在最后read()
一个指示 eof 和cat
终止并关闭管道的读取端之间,实际上有一个小窗口,在此期间进程可以fifo
再次打开写入(并且不会被阻塞,因为仍有读取端) )。然后,如果它在cat
退出之后和另一个进程打开fifo
读取之前写一些东西,它会被一个 SIGPIPE 杀死。