Linux 命名管道:不像想像的那樣先進先出
簡而言之:
mkfifo fifo; (echo a > fifo) &; (echo b > fifo) &; cat fifo
我所期望的:
a b
因為第一個
echo … > fifo
應該是第一個打開文件的,所以我希望該程序是第一個寫入它的程序(首先打開它的解鎖)。我得到什麼:
b a
令我驚訝的是,當打開兩個單獨的終端以在絕對獨立的程序中進行寫入時,也會發生這種行為。
我是否誤解了命名管道的先進先出語義?
斯蒂芬建議添加延遲:
#!/usr/bin/zsh delay=$1 N=$(( $2 - 1 )) out=$(for n in {00..$N}; do mkfifo /tmp/fifo$n (echo $n > /tmp/fifo$n) & sleep $delay (echo $(( $n + 1000 )) > /tmp/fifo$n )& # intentionally using `cat` here to not step into any smartness cat /tmp/fifo$n | sort -C || echo +1 rm /tmp/fifo$n done) echo "$(( $res )) inverted out of $(( $N + 1 ))"
現在,這 100% 正確(
delay = 0.1, N = 100
)。儘管如此,
mkfifo fifo; (echo a > fifo) &; sleep 0.1 ; (echo b > fifo) &; cat fifo
手動執行幾乎總是會產生相反的順序。事實上,即使複製和粘貼
for
循環本身也有一半的時間會失敗。我對這裡發生的事情感到非常困惑。
這與管道的 FIFO 語義無關,也無法證明它們的任何內容。這與 FIFO 在打開時阻塞直到它們被打開以進行寫入和讀取的事實有關。
cat
所以在打開fifo
閱讀之前什麼都不會發生。因為第一個
echo
應該是第一個。在後台啟動程序意味著您不知道它們何時實際被調度,因此不能保證第一個後台程序會在第二個後台程序之前完成它的工作。這同樣適用於解除阻塞程序。
您可以通過人為地延遲第二個程序來提高機率,同時仍然使用後台程序:
rm fifo; mkfifo fifo; echo a > fifo & (sleep 0.1; echo b > fifo) & cat fifo
延遲時間越長,機率越大:
echo a > fifo
等待完成打開的塊fifo
,cat
啟動並打開fifo
哪些解鎖echo a
,然後echo b
執行。然而,這裡的主要因素是何時
cat
打開 FIFO:在此之前,shell 會阻止嘗試設置重定向。最終看到的輸出順序取決於寫入過程被暢通的順序。如果你
cat
先執行,你會得到不同的結果:rm fifo; mkfifo fifo; cat fifo & echo a > fifo & echo b > fifo
這樣一來,
fifo
寫作的開放往往不會阻塞(仍然,沒有保證),所以你會首先看到a
比第一次設置更高的頻率。您還將看到執行cat
前完成,即僅輸出。echo b``a