行程間通訊 (Interprocess Communication)
本系列文章內容參考自經典教材 Operating System Concepts, 10th Edition (Silberschatz, Galvin, Gagne)。本文對應章節:Section 3.4 Interprocess Communication、Section 3.5 IPC in Shared-Memory Systems。
行程間的合作關係
在作業系統中同時執行的行程 (Process),可以分為兩種截然不同的類型:
- 獨立行程 (Independent Process):不與任何其他行程共享資料。這類行程的執行結果不受系統中其他行程的影響,也不影響其他行程。
- 合作行程 (Cooperating Process):能夠影響其他行程、或被其他行程影響。任何與其他行程共享資料的行程都屬於合作行程。
為什麼需要讓行程彼此合作?提供一個允許行程合作的環境,有以下幾個核心理由:
-
資訊共享 (Information Sharing):多個應用程式可能需要存取同一份資料,例如剪貼簿的複製與貼上功能。作業系統必須提供允許多個行程並行存取這些共用資訊的環境。
-
運算加速 (Computation Speedup):若希望某個任務執行得更快,可以將它拆解成多個子任務,讓各子任務在多個處理器核心上並行執行。注意:這個加速效果只有在電腦具備多個處理器核心 (Processing Cores) 時才能實現。
-
模組化 (Modularity):將系統功能設計成獨立的行程或執行緒 (Thread),使整個系統具有更清晰的模組結構,降低各部分之間的耦合度。
合作行程之間需要一個行程間通訊 (Interprocess Communication, IPC) 機制來交換資料,也就是讓行程能互相傳送與接收資料。
3.4 IPC 的兩種基本模型
IPC 有兩種基本模型:共享記憶體 (Shared Memory) 與訊息傳遞 (Message Passing)。
- 共享記憶體模型:合作行程之間建立一塊共用的記憶體區域。行程透過對這塊共用區域的讀寫來交換資訊,資料的格式與存放位置完全由行程自行決定,作業系統幾乎不介入。
- 訊息傳遞模型:作業系統提供一個訊息傳遞設施,讓合作行程透過互相交換訊息 (Message) 來通訊。資料搬移的工作由 OS 的核心 (Kernel) 負責處理。
下圖呈現兩種模型的架構對比:
- 圖中左側 (a) 是共享記憶體模型:Process A 與 Process B 的位址空間中共同包含一塊「shared memory」區域,兩者直接讀寫這塊記憶體來通訊,Kernel 僅在建立這塊共用區域時介入。
- 右側 (b) 是訊息傳遞模型:Process A 與 Process B 不直接共用記憶體,而是透過 Kernel 維護的 message queue 傳送訊息,每次通訊都需要 Kernel 的介入。
這兩種模型各有其適用場景:
| 比較項目 | 訊息傳遞 (Message Passing) | 共享記憶體 (Shared Memory) |
|---|---|---|
| 資料量 | 適合少量資料(避免競爭問題) | 適合大量資料(讀寫速度快) |
| 分散式系統 | 較容易實作 | 實作複雜(需分散式共享記憶體) |
| Kernel 介入 | 每次通訊都需要 | 僅在建立共用區域時需要 |
| 效能 | 較慢(每次 System Call 開銷) | 較快(建立後存取如同一般記憶體) |
兩種模型都被廣泛實作於現代作業系統,許多系統同時支援兩者。訊息傳遞由於不需要衝突避免 (Conflict Avoidance),實作較為簡單,在分散式系統中尤具優勢。共享記憶體一旦建立後,所有存取都被視為普通的記憶體存取,不需要 Kernel 介入,因此速度更快。
Chrome 瀏覽器是行程合作概念的實際應用案例。許多網站包含 JavaScript、Flash、HTML5 等動態內容,若瀏覽器只用一個行程處理所有頁籤,任何一個分頁的網頁應用程式崩潰都會導致整個瀏覽器當掉。
Chrome 以多行程架構 (Multiprocess Architecture) 解決這個問題,識別出三種類型的行程:
- Browser Process(瀏覽器行程):管理使用者介面、磁碟與網路 I/O。Chrome 啟動時建立一個,且只有一個。
- Renderer Process(渲染行程):負責渲染網頁,處理 HTML、JavaScript、圖片等。一般而言,每個新分頁開啟時建立一個新的渲染行程,因此可能同時有多個渲染行程存在。
- Plug-in Process(外掛行程):每種類型的外掛(如 Flash、QuickTime)各對應一個。包含外掛程式碼以及讓外掛與渲染行程、瀏覽器行程通訊的額外程式碼。
這個多行程設計的優點在於:各網站在互相隔離的環境中執行,若某個網站崩潰,只有對應的渲染行程受影響,其他行程維持正常運作。此外,渲染行程運行在沙盒 (Sandbox) 中,對磁碟和網路 I/O 的存取受到限制,也降低了安全漏洞的影響範圍。
3.5 共享記憶體 IPC 的運作方式
共享記憶體區域的建立
以共享記憶體進行 IPC,需要合作行程之間先建立一塊共用的記憶體區域。這塊共用區域通常駐留在建立它的行程的位址空間中,其他想要透過這塊共用區域通訊的行程,則必須將它附加 (Attach) 到自己的位址空間。
這裡有一個關鍵問題:作業系統正常情況下會阻止一個行程存取另一個行程的記憶體,以保護行程的位址空間不被任意讀寫。共享記憶體機制要求兩個或多個行程主動同意移除這個限制,才能讓彼此共用那塊記憶體區域。
一旦共享記憶體建立完成,幾件重要的事由行程自己負責,而非作業系統:
- 資料格式與存放位置:由行程自行約定,OS 不管控。
- 寫入衝突的避免:行程必須自行確保不會在同一時間向相同位置寫入,OS 不提供自動的保護機制(這個同步問題留待 Chapter 6、7 深入探討)。
Producer-Consumer Problem(生產者消費者問題)
為了具體說明合作行程如何透過共享記憶體交換資料,教科書引入了生產者消費者問題 (Producer-Consumer Problem),這是描述合作行程的經典範例。
考慮一個常見的場景:
- Producer(生產者)行程持續產生資料,例如編譯器 (Compiler) 產生組合語言程式碼。
- Consumer(消費者)行程持續消耗這些資料,例如組譯器 (Assembler) 讀取並處理編譯器產出的組合語言程式碼。
- 同理,組譯器又成為生產者,產出目的模組 (Object Modules),由連結器 (Loader) 作為消費者來讀取。
這個模式也對應到客戶端—伺服器範式 (Client-Server Paradigm):伺服器可視為生產者(提供網頁內容),客戶端瀏覽器可視為消費者(請求並顯示網頁)。
核心問題在於: 生產者和消費者必須能夠並行執行,這表示需要一個中間的緩衝區 (Buffer),讓生產者在消費者還沒取用完時就能繼續生產。這個緩衝區駐留在共享記憶體區域中,生產者負責填滿它,消費者負責取走其中的項目。
兩種緩衝區
根據緩衝區是否有大小限制,有兩種設計:
- 無界緩衝區 (Unbounded Buffer):對緩衝區大小沒有實際限制。消費者可能需要等待新項目,但生產者永遠不需要等待,可以持續生產。
- 有界緩衝區 (Bounded Buffer):緩衝區大小固定。若緩衝區已空,消費者必須等待;若緩衝區已滿,生產者也必須等待。
Bounded Buffer 是實際系統中更常見的設計,也是下面具體實作的對象。