Pipe

讀取命名管道:tail 還是 cat?

  • September 18, 2017

我使用了一個文件描述符

mkfifo fifo

一旦有東西寫入這個管道,我想立即重用它。我應該使用

tail -f fifo

或者

while true; do cat fifo; done

?

他們似乎做同樣的事情,我無法衡量性能的差異。但是,當系統不支持 inotify(例如 Busybox)時,需要將前者

tail -f -s 0 fifo

但這會佔用 100% 的 CPU 使用率(測試一下:mkfifo fifo && busybox tail -f -s 0 fifo & echo hi>fifo/ cancel with fg 1and Ctrl``C)。那麼 while-true-cat 是更可靠的解決方案嗎?

當你這樣做時:

cat fifo

假設還沒有其他程序打開fifo寫入,cat將阻塞open()系統呼叫。當另一個程序打開文件進行寫入時,管道將被實例化並open()返回。catread()循環呼叫並read()阻塞,直到其他程序將數據寫入管道。

cat當所有其他寫入程序已將其文件描述符關閉到fifo. 在哪個點cat終止並且管道被破壞¹。

您需要cat再次執行以讀取之後將寫入的內容fifo(但通過不同的管道實例)。

在:

tail -f file

cattail將等待一個程序打開一個文件進行寫入。但是在這裡,由於您沒有-n +1從一開始就指定要複製的內容,tail因此需要等到 eof 才能找出最後 10 行是什麼,因此在寫入結束之前您什麼都看不到。

之後,tail不會關閉它的 fd 到管道,這意味著管道實例不會被破壞,並且仍然會嘗試每秒從管道讀取(在 Linux 上,可以通過使用inotify和某些版本來避免輪詢) GNUtail在那裡做)。這read()將與 eof 一起返回(馬上,這就是為什麼你會看到 100% CPU 的原因-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

cat的 stdin 將同時為讀取和寫入打開,這意味著cat永遠不會在其上看到 eof(即使沒有其他程序打開fifo用於寫入,它也會立即實例化管道)。

在這不起作用的系統上,您可以改為:

cat < fifo 3> fifo

這樣,一旦其他程序打開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 殺死。

引用自:https://unix.stackexchange.com/questions/392697