行程間通訊

在行程間傳遞檔案描述符(passing file descriptors)

檔案描述符不過是一個小整數——fd 3、fd 4——它索引進你行程自己私有的開啟檔案與通訊端表。所以你或許以為可以直接把數字 3 送給另一個行程、它就能用。不行:你行程裡的 fd 3 和它行程裡的 fd 3 指向完全不同的東西,因為每個行程都有自己的描述符表。這個數字在擁有它的行程之外毫無意義。

然而有一個真實、幾乎令人意外的核心功能,能讓一個行程把一個可用的開啟檔案或通訊端交給另一個。在 Unix 網域通訊端上,傳送方用 sendmsg() 把描述符當成「輔助資料」(這個機制叫 SCM_RIGHTS)附上;接收方用 recvmsg() 收下它。真正跨越過去的不是那個整數——核心複製底層的開啟檔案物件,並在接收方的表裡裝上一個全新的描述符(很可能是不同的數字),指向同一個開啟的檔案、通訊端或管線。接收方現在能 read()、write() 它,彷彿是自己開啟的,共用同一個檔案偏移量與連線。

這解鎖了原本不可能的設計。一個有特權的父行程可以開啟一個受限檔案、或綁定一個小編號的連接埠,再把備妥的描述符傳給一個本來絕不可能開啟它的無特權子行程。一個主行程可以 accept() 進來的網路連線,把每個已連線的通訊端交給一群工作行程去服務——這正是某些高效能伺服器分散負載的方式。要記清楚的提醒:這只在同一台機器上、透過 Unix 網域通訊端才有效,而且它轉移的是一項能力(一個開啟的連線),不是路徑或名字——接收方拿到的是活生生的物件,連同全新的描述符編號。

父行程為一個網路連線開啟 fd,再透過 Unix 通訊端用帶 SCM_RIGHTS 的 sendmsg() 送出 → 子行程的 recvmsg() 取得比方說 fd 7,指向同一個連線。子行程直接服務它;旅行過去的從來不是整數 3。

核心把開啟的物件複製進接收方的表。描述符編號改變了;底層的連線是共用的。

你無法靠單純送出描述符的整數值來傳遞它——那個數字索引的是逐行程的表,在別處毫無意義。真正的 fd 傳遞需要在 Unix 網域通訊端上用 SCM_RIGHTS,而到達的是指向同一個開啟物件的新編號。

又稱
fd passingSCM_RIGHTSdescriptor passing檔案描述符傳遞