對守護程序使用 fifo
在學習 Richard Stevens 的《Unix 網路程式》一書時,我遇到了以下關於在客戶端和伺服器之間使用 FIFO 的內容。
客戶端程序啟動並打開 FIFO 進行寫入、寫入請求並退出。每次客戶端程序終止時,讀取都會向守護程序返回零。然後,守護程序必須再次打開 FIFO(只讀)並在此處等待,直到客戶端程序打開它進行寫入。
我沒看懂最後一行。為什麼伺服器程序必須再次打開 FIFO,它只需要在客戶端程序寫入後再次讀取,對嗎?
為什麼伺服器程序必須再次打開 FIFO,它只需要在客戶端程序寫入後再次讀取,對嗎?
有趣,讓我們試試你的建議。以下結果是在 Linux 4.9.0-6-amd64(Ubuntu Linux 核心)上生成的。
$ mkfifo t $ (cat; cat) < t & # run a "server" as a background job [1] 4856 $ echo 1 > t 1 [1]+ Done ( cat; cat ) < t
它沒有按我們的意願工作。第一個
cat
按預期讀取 EOF,然後退出。問題是第二個cat
也立即讀取 EOF,因此我們的“伺服器”完成了。無法等待新客戶端(無需重複呼叫 read() 並浪費 CPU 時間)。如果您知道如何在 shell 中操作文件描述符 (FD),我們可以通過另一種方式來幫助確認它。
$ echo 1 > t & $ exec 3 < t # open "t" for reading, as FD 3. $ cat <&3 1 [1]+ Done echo 1 > t $ cat <&3 $ $ echo 2 > t & [1] 5102 $ cat <&3 2 [1]+ Done echo 2 > t $ cat <&3 $
答案是重新
open()
啟用fifo 所達到的目的是阻止其他人打開它進行寫入。如果沒有這一步,所有來自 fifo 的後續呼叫read()
將立即返回 0 (EOF)。當我注意到這一點時,我想知道它是如何
systemd-initctl
工作的。該程序在 systemd 下模擬舊的/dev/initctl
fifo。(免責聲明:測試它不是很容易;我不會費心去記錄如何)。答案是 systemd-initctl 為讀寫打開 fifo。(從技術上講,它是 systemd 打開 fifo,由 systemd-initctl.socket 指定,並將其傳遞給 systemd-initctl)。為同時讀取和寫入而打開一個 fifo 是 Linux 特有的功能。但是通過這樣做,systemd 正在實現與 Stevens 接下來提到的相同的技巧:為了避免這種情況,一個有用的技術是讓守護程序打開 FIFO 兩次——一次用於讀取,一次用於寫入。返回用於讀取的文件描述符用於讀取客戶端請求,而用於寫入的文件描述符從不使用。通過讓 FIFO 始終為寫入打開(只要守護程序存在),讀取不會返回 EOF,而是等待下一個客戶端請求。