Daemon

對守護程序使用 fifo

  • June 20, 2018

在學習 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/initctlfifo。(免責聲明:測試它不是很容易;我不會費心去記錄如何)。答案是 systemd-initctl 為讀寫打開 fifo。(從技術上講,它是 systemd 打開 fifo,由 systemd-initctl.socket 指定,並將其傳遞給 systemd-initctl)。為同時讀取和寫入而打開一個 fifo 是 Linux 特有的功能。但是通過這樣做,systemd 正在實現與 Stevens 接下來提到的相同的技巧:

為了避免這種情況,一個有用的技術是讓守護程序打開 FIFO 兩次——一次用於讀取,一次用於寫入。返回用於讀取的文件描述符用於讀取客戶端請求,而用於寫入的文件描述符從不使用。通過讓 FIFO 始終為寫入打開(只要守護程序存在),讀取不會返回 EOF,而是等待下一個客戶端請求。

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