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 杀死。