跳至主要内容

IPC 的具體實作 (Examples of IPC Systems)

備註

本系列文章內容參考自經典教材 Operating System Concepts, 10th Edition (Silberschatz, Galvin, Gagne)。本文對應章節:Section 3.7 Examples of IPC Systems

Section 3.4 與 3.5 介紹了 IPC 的兩種基本模型(共享記憶體與訊息傳遞)及抽象概念,Section 3.7 則以四個具體系統為例,說明這些模型在真實環境中的實作方式:

IPC 系統模型核心機制
POSIX Shared Memory共享記憶體記憶體映射檔案,讓多個行程的虛擬位址指向同一塊實體記憶體
Mach訊息傳遞以 Port 為通訊端點,利用虛擬記憶體映射取代資料複製
Windows ALPC訊息傳遞(含共享記憶體)依訊息大小自動選擇三種傳遞策略
Pipes資料流最早的 UNIX IPC 機制,以檔案描述符為介面的單向資料通道

3.7.1 POSIX 共享記憶體

記憶體映射檔案的概念

Section 3.5 介紹了共享記憶體的抽象概念:兩個行程必須建立一塊共用的記憶體區域才能通訊。但在實作層面有一個根本問題:一個行程要如何「找到」另一個行程建立的記憶體區域? 若直接分配記憶體,這塊區域只存在於建立它的行程的位址空間中,其他行程根本不知道要去哪裡找它。

POSIX 共享記憶體以記憶體映射檔案 (Memory-Mapped File) 解決這個問題:給共享記憶體區域一個名稱 (Name),讓這個名稱像一個檔案一樣存在於系統中。任何行程只要知道這個名稱,就能透過 mmap() 將其映射到自己的虛擬位址空間,OS 會確保兩個行程的指標都指向同一塊實體記憶體。這樣一來,行程之間的通訊就等同於直接讀寫自己的記憶體,不需要 Kernel 中介,速度極快。

三個關鍵 API

建立 POSIX 共享記憶體區域需要三個系統呼叫,每個負責不同的職責:

第一步:建立(或開啟)共享記憶體物件

fd = shm_open(name, O_CREAT | O_RDWR, 0666);

shm_open() 的行為與 open() 開啟一般檔案完全相同,差別是它操作的對象是共享記憶體物件而非磁碟上的實體檔案。

  • name:共享記憶體物件的識別名稱,其他行程必須用相同的名稱才能開啟同一塊共享記憶體。
  • O_CREAT | O_RDWR:「若不存在則建立,開啟讀寫權限」。消費者行程用 O_RDONLY 以唯讀方式開啟。
  • 呼叫成功後回傳一個整數檔案描述符 (File Descriptor)

第二步:設定大小

ftruncate(fd, 4096);

新建立的共享記憶體物件大小為零,必須用 ftruncate() 指定大小(此例為 4096 bytes)。

第三步:映射到位址空間

ptr = (char *) mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

mmap() 將共享記憶體物件映射到行程的虛擬位址空間,回傳指標 ptr。之後對 ptr 的所有讀寫直接等同於對共享記憶體的讀寫,效率等同一般記憶體存取。MAP_SHARED 旗標確保對這塊記憶體的修改對所有共享此物件的行程立即可見。

下圖說明生產者與消費者各自呼叫 mmap() 後,兩個行程的虛擬指標如何指向同一塊共享記憶體物件:

POSIX Shared Memory — 兩個行程透過 mmap() 共享同一塊記憶體物件

生產者在自己的虛擬位址空間中取得一個 ptr,消費者也在自己的空間中取得另一個 ptr,但這兩個指標底層都映射到同一塊實體記憶體。生產者對 ptr 寫入的資料,消費者的 ptr 能直接讀到,整個過程完全不需要 Kernel 複製資料。這正是共享記憶體在大量資料傳輸時比訊息傳遞更有效率的原因。

Producer-Consumer 完整範例

以下是生產者(建立共享記憶體並寫入字串)與消費者(讀取後移除物件)的完整程式碼。

生產者:

const int SIZE = 4096;
const char *name = "OS";
int fd;
char *ptr;

/* 建立共享記憶體物件 */
fd = shm_open(name, O_CREAT | O_RDWR, 0666);
/* 設定大小 */
ftruncate(fd, SIZE);
/* 映射到位址空間 */
ptr = (char *) mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

/* 寫入資料:ptr 指標直接當成記憶體使用 */
sprintf(ptr, "%s", "Hello");
ptr += strlen("Hello");
sprintf(ptr, "%s", "World!");

消費者:

const int SIZE = 4096;
const char *name = "OS";
int fd;
char *ptr;

/* 開啟已存在的物件(唯讀) */
fd = shm_open(name, O_RDONLY, 0666);
/* 映射到位址空間 */
ptr = (char *) mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
/* 讀取 */
printf("%s", (char *) ptr);
/* 移除共享記憶體物件 */
shm_unlink(name);
shm_unlink() 的語意

shm_unlink() 移除共享記憶體物件的名稱,使其無法再被新行程開啟。但若仍有行程持有開啟的描述符或現有的映射,這塊記憶體的內容不會立即消失,直到所有行程都關閉或解除映射為止。這與 UNIX 檔案系統的 unlink() 語意相同:刪除名稱,但保留資料直到所有參考都結束。


3.7.2 Mach 訊息傳遞

Mach 的設計哲學

Mach 是一個專為分散式系統 (Distributed Systems) 設計的作業系統核心,後來也被證明適用於桌上型與行動裝置(macOS 和 iOS 都以 Mach Kernel 為基礎,如 Section 2.8 所述)。Mach 的核心設計原則是:幾乎所有通訊都透過訊息傳遞完成,包括所有行程間(在 Mach 中稱為 Task 間)的互動。

Task 與 Process 的差異

Mach 中的 Task 類似於一般作業系統的行程,但包含多個執行緒 (Thread) 且關聯的資源較少。可以把 Task 想像成輕量化的行程容器,內部的多個執行緒共享 Task 的所有資源,包括 Port Rights。

Port 與 Port Rights

Mach 以 Port(通訊埠) 作為訊息傳遞的端點。理解 Port 最直觀的方式,是把它想像成一個實體郵箱 (Mailbox)

  • 郵箱本身(Port) 屬於能「拿鑰匙開信箱、取出信件」的那個人。
  • 其他人持有的投件權限讓他們能「把信件投入信箱門縫」,但無法開箱讀信。

這個比喻對應到 Mach 的 Port Rights(通訊埠權限) 機制:

Port Right對應郵箱比喻說明
MACH_PORT_RIGHT_RECEIVE信箱鑰匙持有此權限才能從 Port 取出(接收)訊息。同一個 Port 只能有一個 RECEIVE right 持有者,即 Port 的唯一擁有者 (owner),也就是建立這個 Port 的 Task
MACH_PORT_RIGHT_SEND投件權限持有此權限才能向 Port 放入(傳送)訊息。擁有者可以把這個權限授予給任意多個 Task

Port Rights 的範圍是 Task 層級,同一個 Task 內的所有執行緒共享相同的 Port Rights。

「多對一」的直覺解釋

Port 的所有特性都源於「一把鑰匙、多個投件人」這個設計:

  • 一個接收者(one receiver):RECEIVE right 只有一份,只有 Port 的擁有者能讀訊息,這就是「一」的那一側。
  • 多個傳送者(many senders):SEND right 可以被分發給任意多個 Task,這些 Task 的訊息都會依序進入 Port 的訊息佇列。這就是「多」的那一側。
現實場景:

Server Task 建立 Port P_server 並持有 RECEIVE right(信箱擁有者)。System 透過 Bootstrap Server 讓所有 Client 都拿到 P_server 的 SEND right(投件權限)。每個 Client 都能向 P_server 投送請求,所有請求依序排入 Queue,但只有 Server 自己能逐一取出並處理。這正是「多個 Client 對應一個 Server Port」的結構。

Port 的其他特性

  • 有限大小 (Finite):訊息佇列的容量有限,若已滿,傳送方必須等待或選擇其他策略(後面會介紹四種選項)。
  • 單向 (Unidirectional):一個 Port 只能「從外部收進訊息、擁有者讀出訊息」,資料流只有一個方向。若要雙向通訊,必須建立兩個 Port,一個給 T1 接收、另一個給 T2 接收。

以具體場景說明:Task T1 擁有 Port P1,它要傳訊息給 Task T2 擁有的 Port P2,並期望 T2 回覆。T1 必須先將 P1 的 MACH_PORT_RIGHT_SEND 授予 T2,T2 才有權限回覆到 P1。

下圖展示這個雙 Port 通訊模型的完整結構:T1 持有 P1(RECEIVE right)並持有 P2 的 SEND right;T2 持有 P2(RECEIVE right)並持有 P1 的 SEND right(由 T1 在訊息 Header 中授予):

Mach Port Communication — 雙 Port 雙向通訊模型

圖中兩支箭頭揭示了 Mach 雙向通訊的核心機制:T1 傳送訊息時,在 msgh_remote_port 填入目的地 P2,同時在 msgh_local_port 填入自己的 P1 作為「回信地址 (return address)」。T2 收到訊息後,從 Header 讀取這個回信地址,再用它向 P1 傳送回覆。這正是為什麼 Mach 的雙向通訊必須使用兩個 Port:一個接收請求,另一個接收回覆,兩者方向不同,各自有獨立的 RECEIVE 所有權。

系統保留 Port

每個 Task 建立時,Mach Kernel 自動建立兩個特殊 Port:

Port用途
Task Self PortKernel 持有此 Port 的接收權,允許 Task 傳訊息給 Kernel(用於系統呼叫等)
Notify PortTask 持有此 Port 的接收權,Kernel 可透過此 Port 傳送事件通知給 Task

Port 的建立

mach_port_allocate() 建立一個新 Port 並分配其訊息佇列空間:

mach_port_t port;  // Port 的名稱(整數值,類似 UNIX 的 file descriptor)

mach_port_allocate(
mach_task_self(), // 指向自身 Task
MACH_PORT_RIGHT_RECEIVE, // 建立具有接收權的 Port
&port // 回傳 Port 名稱
);

Port 的「名稱」是一個簡單的整數值,行為就像 UNIX 的 file descriptor。每個 Task 還有一個 Bootstrap Port,可透過它向系統全域的 Bootstrap Server 登記 Port,讓其他 Task 查詢後取得傳送訊息所需的權限。

訊息結構

Mach 訊息由兩部分組成:

部分大小內容
Header(標頭)固定大小訊息大小、來源 Port、目的地 Port 等後設資料 (Metadata)
Body(本體)可變大小實際資料

訊息分為兩種類型:

  • Simple Message(簡單訊息):本體包含普通的非結構化使用者資料,Kernel 不解讀其內容。
  • Complex Message(複雜訊息):本體可包含指向記憶體位址的指標(稱為 "out-of-line" 資料),或用於轉移 Port Rights 給另一個 Task。Out-of-line 資料特別適合傳輸大量資料:不需複製資料本身,只需傳遞一個指標,接收方自行從指定位址讀取。

訊息傳送與接收:mach_msg()

mach_msg() 是 Mach 訊息傳遞的標準 API,同時用於傳送與接收,透過參數旗標 MACH_SEND_MSGMACH_RCV_MSG 決定操作方向:

/* Client:建立並傳送訊息 */
struct message msg;
msg.header.msgh_size = sizeof(msg);
msg.header.msgh_remote_port = server; // 目的地 Port
msg.header.msgh_local_port = client; // 回覆用 Port("return address")

mach_msg(&msg.header,
MACH_SEND_MSG, // 傳送操作
sizeof(msg), // 傳送訊息的大小
0, // 接收緩衝大小(傳送時不需要)
MACH_PORT_NULL, // 接收用 Port(傳送時不需要)
MACH_MSG_TIMEOUT_NONE, // 不設逾時
MACH_PORT_NULL); // 不需要 Notify Port

/* Server:接收訊息 */
mach_msg(&msg.header,
MACH_RCV_MSG, // 接收操作
0, // 傳送大小(接收時不需要)
sizeof(msg), // 接收緩衝的最大大小
server, // 要在哪個 Port 上等待
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);

呼叫 mach_msg() 後,內部會呼叫 mach_msg_trap() 這個系統呼叫進入 Kernel 模式,完成實際的訊息傳遞。Header 中的 msgh_local_port 欄位作為「回信地址」傳給接收方,接收方可以用它向傳送方回覆。

以下序列圖展示 Client 與 Server 之間一次完整的請求—回覆交換流程,以及每個步驟對應的 mach_msg() 呼叫:

實際通訊並非 T1 和 T2 直接連線,Kernel 在兩者之間負責把訊息放入對應的 Port 佇列,再喚醒等待中的接收方。序列圖以 T1 → T2 的箭頭簡化表示這個「透過 Kernel 轉送」的過程。

Port Queue 已滿時的四種選項

若目的地 Port 的訊息佇列已滿,mach_msg() 的行為由參數控制,有四種選項:

  1. 無限等待:阻塞直到佇列有空位。
  2. 有限等待:等待最多 n 毫秒,逾時後返回錯誤。
  3. 立刻返回:不等待,直接返回失敗。
  4. 暫時快取:將訊息交給 OS 暫存,待佇列有空位後 OS 自動送出,並傳送一則通知訊息給傳送方。每個傳送方執行緒最多只能有一則訊息在此暫存狀態中等待。

第四種選項是為 Server Task 設計的:Server 處理完一個請求後可能需要回覆,但不能因為 Reply Port 已滿而停下來等待,否則其他請求也得不到服務。這個「暫時快取」機制讓 Server 能繼續處理新請求,由 OS 在背景完成回覆訊息的最終送達。

虛擬記憶體優化:避免資料複製

傳統訊息系統的效能瓶頸在於資料複製:訊息必須從傳送方的緩衝區複製到接收方的緩衝區,既消耗記憶體也消耗 CPU 時間。

Mach 利用虛擬記憶體管理 (Virtual Memory Management) 技術解決這個問題:不真正複製訊息內容,而是把含有訊息的傳送方位址空間區段直接映射 (Map) 到接收方的位址空間中。兩者實際上存取的是同一塊實體記憶體,訊息從未被複製過。這個技術帶來大幅的效能提升,但僅適用於同一機器內的訊息傳遞(Intrasystem Messages);跨機器的分散式通訊仍需進行實際的網路傳輸。


3.7.3 Windows IPC (ALPC)

Windows 採用模組化設計,提供多種作業環境(Subsystem)。應用程式透過訊息傳遞機制與各子系統伺服器通訊,從這個角度看,應用程式是子系統伺服器的客戶端。

Windows 的本地訊息傳遞設施稱為 ALPC(Advanced Local Procedure Call,進階本地程序呼叫),專門用於同一台機器上兩個行程之間的通訊。概念上類似廣泛使用的 RPC(Remote Procedure Call,遠端程序呼叫),但針對 Windows 本地通訊進行了優化。

Connection Ports 與 Communication Ports

ALPC 以 Port 物件 (Port Object) 建立與維護行程間的連線,有兩種類型:

  • Connection Port(連線埠):由 Server 行程公開,所有行程均可見。Client 向 Connection Port 發送連線請求,作為通訊的起點。
  • Communication Port(通訊埠):Server 接受連線後建立一個私有的通訊通道 (Channel),通道包含一對 Communication Ports,一個給 Client,另一個給 Server,後續的所有訊息都透過這對 Communication Ports 傳送。

通訊通道還支援回呼機制 (Callback Mechanism),允許 Client 與 Server 在預期等待回覆時也能接受對方發來的請求,增加通訊的靈活性。

下圖展示 ALPC 從連線建立到訊息傳遞的完整結構:

Figure 3.19 — Advanced local procedure calls in Windows

圖中 Client 向 Server 的 Connection Port 發送連線請求(圖上方的箭頭),Server 建立通道並回傳 Handle:Client 得到 Client Communication Port 的 Handle,Server 持有 Server Communication Port 的 Handle。此後兩端透過這對 Communication Ports 互相傳送訊息。若訊息超過 256 bytes,通道另外提供 Shared Section Object(下方黃色區塊)讓兩端存取同一塊記憶體,避免資料複製。

三種訊息傳遞技術

ALPC 通道建立時,依照預期訊息大小選擇三種技術之一:

  1. Port Message Queue(訊息佇列):適用於小訊息(≤ 256 bytes)。訊息直接複製到 Port 的訊息佇列,由 OS 負責搬移到接收行程。
  2. Section Object(共享記憶體區段):適用於大訊息(> 256 bytes)。Server 或 Client 建立一塊共享記憶體區域 (Section Object) 作為通道的一部分,傳送時只傳遞一個包含指標與大小資訊的小訊息,接收方直接從共享記憶體讀取,避免複製大量資料。
  3. 直接讀寫 (Direct Read/Write):適用於資料量超大到不適合用 Section Object 的情況。Server 行程直接讀寫 Client 的位址空間,省去 Section Object 的建立開銷。

Client 在建立通道時就決定是否需要 Section Object;若 Server 預期回覆訊息也很大,Server 也可以建立自己的 Section Object。

ALPC 對應用程式不可見

ALPC 不是 Windows API 的一部分,應用程式無法直接呼叫它。應用程式使用 Windows API 發出的 RPC 呼叫,當目標是同一機器上的行程時,Windows 內部自動透過 ALPC 處理。許多 Kernel 服務也使用 ALPC 與客戶端行程通訊。


3.7.4 Pipes(管線)

Pipe 是最早的 UNIX IPC 機制之一,提供兩個行程之間資料流通訊 (Stream Communication) 的方式。與共享記憶體不同,Pipe 不讓行程直接讀寫對方的記憶體,而是由 OS 提供一個單向資料通道:資料從一端寫入,從另一端讀出,OS 自動處理緩衝與同步。

Pipe 的四個設計考量

在設計或選擇 Pipe 時,必須回答以下四個問題:

  1. 通訊方向:Pipe 是單向 (Unidirectional) 還是雙向 (Bidirectional)?
  2. 雙工模式:若允許雙向通訊,是半雙工 (Half-Duplex,同一時間只能一個方向傳輸) 還是全雙工 (Full-Duplex,兩個方向可同時傳輸)?
  3. 行程關係:通訊的兩端行程是否必須存在親子關係 (Parent-Child Relationship)?
  4. 網路支援:Pipe 只能在同一台機器上的行程間使用,還是可以跨網路?

不同類型的 Pipe 對這四個問題有不同的答案,後面的比較表會整理這些差異。


Ordinary Pipes(普通管線)

Ordinary Pipe 是最基本的 Pipe 形式,允許兩個行程以生產者—消費者 (Producer-Consumer) 模式通訊:生產者從 write end 寫入資料,消費者從 read end 讀取資料。

Ordinary Pipe 的關鍵限制是:它是單向的,且只能在有親子關係的行程間使用。若需要雙向通訊,必須建立兩個 Pipe,各負責一個方向。

UNIX 的 Ordinary Pipe

UNIX 系統以 pipe() 函式建立 Ordinary Pipe:

pipe(int fd[]);

pipe() 建立一個 Pipe,並透過兩個檔案描述符 (File Descriptor) 存取:

  • fd[0]read end(讀取端),消費者從這裡讀資料
  • fd[1]write end(寫入端),生產者向這裡寫資料

UNIX 把 Pipe 視為一種特殊檔案,因此可以直接用 read()write() 系統呼叫操作,無需學習新的 API。

Ordinary Pipe 無法在建立它的行程以外被存取。典型用法是:父行程先呼叫 pipe() 建立 Pipe,再呼叫 fork() 建立子行程;由於子行程從父行程繼承所有已開啟的檔案(包括 Pipe),父子行程因此共享這條 Pipe 的兩個端口。

下圖展示 fork() 後父子行程與 Pipe 檔案描述符的關係:

Figure 3.20 — File descriptors for an ordinary pipe

fork() 之後,父行程與子行程都各自持有 fd[0](read end)和 fd[1](write end)這兩個描述符,且都指向同一條 Pipe。若要讓父行程寫入、子行程讀取,每一方都必須關閉自己不使用的那一端:父行程關閉 fd[0](read end),子行程關閉 fd[1](write end)。

這個步驟之所以必要,而非只是好習慣,原因在於 Pipe 的 EOF 語意read() 只有在 Pipe 的所有 write end 描述符都關閉後,才會回傳 0(end-of-file),讀取方才能得知資料已全部傳輸完畢。以上述「父行程寫入、子行程讀取」的情境為例:即使父行程寫完後關閉了自己的 fd[1],若子行程沒有事先關閉自己的 fd[1],Pipe 仍有一個 write end 開著(子行程自己手上的那個),系統認為隨時可能再有資料寫入,子行程的 read() 就永遠收不到 EOF,導致永遠阻塞。

以下是完整範例(父行程寫入 "Greetings",子行程讀取並列印):

#define BUFFER_SIZE 25
#define READ_END 0
#define WRITE_END 1

int main(void) {
char write_msg[BUFFER_SIZE] = "Greetings";
char read_msg[BUFFER_SIZE];
int fd[2];
pid_t pid;

if (pipe(fd) == -1) {
fprintf(stderr, "Pipe failed");
return 1;
}

pid = fork();

if (pid > 0) { /* parent: writes to pipe */
close(fd[READ_END]);
write(fd[WRITE_END], write_msg, strlen(write_msg) + 1);
close(fd[WRITE_END]);
} else { /* child: reads from pipe */
close(fd[WRITE_END]);
read(fd[READ_END], read_msg, BUFFER_SIZE);
printf("read %s", read_msg);
close(fd[READ_END]);
}
return 0;
}

Windows 的 Ordinary Pipe(Anonymous Pipe)

Windows 的 Ordinary Pipe 稱為 Anonymous Pipe(匿名管線),行為與 UNIX 版本相似:單向傳輸,需要親子行程關係,讀寫使用 ReadFile()WriteFile()

建立 Anonymous Pipe 使用 CreatePipe() 函式,接受四個參數:

  1. read handle(讀取端 Handle)的輸出參數
  2. write handle(寫入端 Handle)的輸出參數
  3. SECURITY_ATTRIBUTES 結構,用於設定子行程是否可繼承這些 Handle
  4. Pipe 的大小(bytes)

與 UNIX 不同,Windows 子行程不會自動繼承父行程的 Handle,程式設計師必須明確在 SECURITY_ATTRIBUTES 中設定允許繼承,並在 CreateProcess() 中傳入 TRUE 告知新行程可繼承指定 Handle。由於 Anonymous Pipe 是半雙工,父行程還必須使用 SetHandleInformation() 禁止子行程繼承 write end 的 Handle,確保 Pipe 的方向性。


Named Pipes(具名管線)

Ordinary Pipe 只能在有親子關係的行程間使用,且僅在通訊期間存在,行程結束後 Pipe 就消失。Named Pipe(具名管線) 突破了這兩個限制。

Named Pipe 的核心優勢在於擁有一個在檔案系統中可見的名稱:任何知道這個名稱的行程都可以開啟它,無需親子關係,也不需要在同一個 Session 中。一個 Named Pipe 可以有多個寫入方,且在所有通訊行程結束後仍繼續存在,直到被明確刪除為止。

UNIX 的 Named Pipe(FIFO)

UNIX 系統中,Named Pipe 稱為 FIFO,以 mkfifo() 系統呼叫建立。FIFO 在檔案系統中以一般檔案的形式出現,可用 open()read()write()close() 操作,直到被明確 unlink() 刪除為止。

UNIX FIFO 的限制:

  • 半雙工:雖然可以雙向通訊,但同一時間只能一個方向傳輸。若需要真正的雙向通訊,通常建立兩個 FIFO。
  • 僅限同一機器:通訊行程必須在同一台機器上。若需要跨機器通訊,必須使用 Socket(Section 3.8.1)。
  • 位元組導向 (Byte-Oriented):只能傳輸位元組串流,不支援訊息邊界。

Windows 的 Named Pipe

Windows 的 Named Pipe 功能比 UNIX 版本豐富:

  • 支援全雙工 (Full-Duplex):兩個方向可同時傳輸。
  • 支援跨網路通訊:通訊行程可在同一台或不同機器上。
  • 既支援位元組導向也支援訊息導向 (Message-Oriented) 資料:訊息導向模式下接收方能取得完整的訊息單元,而非需要自己從位元組串流中拆解。

Windows Named Pipe 以 CreateNamedPipe() 建立,客戶端以 ConnectNamedPipe() 連接,讀寫使用 ReadFile()WriteFile()

Ordinary Pipe 與 Named Pipe 的比較

比較項目Ordinary PipeNamed Pipe (UNIX FIFO)Named Pipe (Windows)
壽命僅在通訊期間直到明確刪除直到明確刪除
行程關係必須有親子關係不需要不需要
通訊方向單向半雙工全雙工
跨網路不支援不支援支援
資料模式位元組位元組位元組或訊息

Pipes 在實務中的應用

Pipe 在 UNIX 命令列環境中極為普遍。Shell 以 | 符號將一個命令的標準輸出直接連到下一個命令的標準輸入,每個命令是一個獨立行程,行程之間以 Pipe 連結:

ls | less

ls 是生產者(將目錄列表寫入 Pipe 的 write end),less 是消費者(從 read end 讀取內容逐頁顯示)。Windows 命令提示字元也支援相同的 | 語法(例如 dir | more),概念完全一致。