行程間通訊

匿名管線(anonymous pipe)

把它想成一段花園水管:你從一端灌水,水就從另一端流出,依序、單向。匿名管線對位元組來說正是如此。一個行程把位元組寫進一端,另一個行程從另一端讀出同樣的位元組,先進先出,資料單向流動。這是兩個有親屬關係的行程傳遞資料流最古老、最簡單的方式。

具體來說,行程呼叫 pipe(),核心回傳兩個檔案描述符:fd[0] 是讀取端,fd[1] 是寫入端。它們的行為跟任何檔案描述符一樣——你對 fd[0] 做 read()、對 fd[1] 做 write()——但磁碟上沒有檔案;緩衝區位於核心裡。讓它對 IPC 有用的訣竅是 fork():父行程先建立管線,再 fork,於是子行程繼承了兩個描述符的副本。慣例上父行程關掉自己用不到的那一端、子行程關掉另一端,留下一條乾淨的單向通道。這就是它被稱為「匿名」的原因——它在檔案系統裡沒有名字,所以只有繼承到描述符的行程(父行程及其後代)才找得到它。無親屬關係的行程找不到。

管線承載的是原始位元組流,沒有訊息邊界:如果寫入方做兩次各 10 位元組的 write(),讀取方可能在一次 read() 收到全部 20 位元組,也可能先 5 再 15——管線不保留位元組原本是怎麼分組的。核心緩衝區容量固定(Linux 上常見為 64 KiB);填滿時 write() 會阻塞,直到讀取方排掉一些,這就是天然的背壓。還有一個明顯的危險:若所有讀取端都已關閉而某行程仍在寫入,它會收到 SIGPIPE 信號(預設動作:終止寫入方)。

int fd[2]; pipe(fd); if (fork() == 0) { close(fd[1]); /* 子行程讀取 */ read(fd[0], buf, n); } else { close(fd[0]); /* 父行程寫入 */ write(fd[1], "hi", 2); }

在 fork 之前先建立管線;雙方各自關掉用不到的那一端。剩下的那一對就是通道。

匿名管線是單向的。要雙向對話需要兩條管線(或一個 socketpair)。又因為它在檔案系統裡沒有名字,它只能連接一個行程與它自己的親屬——絕不可能連起兩個無關的程式。

又稱
pipeunnamed pipe管線無名管線