天天看點

UNIX(程序間通信):18 如何使用多程序協作來解決問題

使用多程序協作來實作應用和系統是一種被廣泛使用的開發方法。多程序協作主要有以下三點優勢。

  • 将功能子產品化,避免重複造輪子。
  • 增強子產品間的隔離,提供更強的安全保障。
  • 提高應用的容錯能力。

程序間通信(Inter-Process Communication,IPC)則是多程序協作的基礎。一般而言,IPC至少需要兩方(如兩個程序)參與。根據資訊流動的方向,這兩方通常被稱為發送者和接收者。在實際使用中,IPC經常被用于服務調用,是以參與IPC的兩方又被稱為調用者和被調用者,或者用戶端和服務端。

圖7-1是一個簡單的IPC設計。它假設核心已為兩個程序映射了一段共享記憶體,且共享記憶體剛好可以存放兩個消息(發送者消息和接受者消息)。

01 程序間通信的重要功能

  • 資料傳遞:消息傳遞(message passing)是IPC中常用的資料傳遞方式,即将資料抽象成一個個的消息進行傳遞。不同的IPC設計有不同的消息抽象,且消息傳遞往往需要一個“中間人”(如共享記憶體)。
  • 控制流轉移:當一個通信發生時,核心将控制流從發送者程序切換到接收者程序(傳回的過程類似)。IPC中的控制流轉移,通常是利用核心對程序的運作狀态和運作時間的控制來實作的。

02 程序間通信的分類

  • 單向IPC、雙向IPC、單/雙向IPC:單向IPC通常指消息在一個連接配接上隻能從一端發送到另一端,雙向IPC則允許雙方互相發送消息。而單/雙向IPC則會根據通信中具體的配置選項等來判斷是否需要支援單向或雙向的通信。實際中,很多系統選擇的是單/雙向IPC,這樣可以比較好地支援各種場景。當然,如管道、信号等隻支援單向IPC的機制在實際中同樣有較多的應用。
  • 同步IPC和異步IPC:簡單來看,同步IPC指它的IPC操作(如Send)會阻塞程序直到該操作完成;而異步IPC則通常是非阻塞的,程序隻要發起一次操作即可傳回,而不需要等待其完成。相比異步而言,同步IPC有着更好的程式設計抽象。然而同步IPC在作業系統的發展中,逐漸表現出一些不足。一個典型的問題是并發。總的來看,目前大部分作業系統核心都會選擇同時實作同步和異步IPC,以滿足不同的應用需求。

03 程序間通信的相關機制

  • 逾時機制:逾時機制擴充了IPC通信雙方的接口,允許發送者/接收者指定它們發送/接收請求的等待時間。比如,一個應用程式可以花費5秒等待檔案系統程序的IPC請求處理操作。如果超過5秒仍然沒有回報,則由作業系統核心結束這次IPC調用,傳回一個逾時的錯誤。
  • 通信連接配接管理:對于基于共享記憶體的程序間通信方案,通信連接配接的建立通常是在建立共享區域的一瞬間完成的;而對于涉及核心的控制流轉移的通信而言,通信連接配接管理是核心IPC子產品的很重要的一部分。雖然實際的系統中會有各種不同的實作,但是它們大部分可以被歸為兩類——直接通信和間接通信。直接通信是指通信的程序一方需要顯式地辨別另一方。間接通信需要經過一個中間的信箱來完成通信,每個信箱有自己唯一的辨別符,而程序間通過共享一個信箱來交換消息。
  • 權限檢查:程序間通信通常依賴于一套權限檢查的機制來保證連接配接的安全性。例如,seL4等微核心系統中的Capability機制,會将所有的通信連接配接抽象成一個個的核心對象。而每個程序對核心對象的通路權限(以及能夠在該核心對象上執行的操作)由Capability來刻畫。當一個程序企圖和某其他程序通信時,核心會檢查該程序是否擁有一個Capability,是否有足夠的權限通路一個連接配接對象并且對象是指向目标程序的。類似地,宏核心,如Linux系統,通常會複用其有效使用者/有效組的檔案權限,以刻畫程序對于某個連接配接的權限。
  • 命名服務:命名服務像是一個全局的看闆,可以協調服務端程序和用戶端程序之間的資訊。簡單來說,服務端程序可以将自己提供的服務告訴命名服務程序,比如檔案系統程序可以注冊一個“檔案系統服務”,網絡系統程序可以注冊一個“網絡服務”。而用戶端程序可以去命名服務上查詢目前的服務,并選擇自己希望建立連接配接的服務去嘗試擷取權限。具體是否分發權限給對應的用戶端程序,是由命名服務和對應的服務端程序根據特定的政策來判斷的。

04 宏核心程序間通信

宏核心下的典型的程序間通信機制,具體包括管道,System V中的消息隊列、信号量、共享記憶體,Linux信号機制,以及套接字機制(socket)。

宏核心作業系統中程序間通信更多的是應用之間的互動,是以,設計的重心通常會放在接口的易用性、穩定性等方面。圖7-5給出了典型的宏核心程序間通信機制的對比。可以看到,雖然在IPC的幾個設計角度上幾個方案都各有異同,但是它們之間的主要差別是在資料抽象上。在實際的應用中,雖然多種IPC方案都可以作為通信的選擇,但是應用程式往往會根據對資料抽象的需求來選擇具體的方案。

05 微核心程序間通信

由于程序間通信對于微核心系統性能的重要意義,大部分微核心作業系統都會優先從性能角度來設計和實作程序間通信。

Mach:早期的微核心程序間通信設計

Mach通過兩種基本的抽象——端口(port)和消息(message),設計和實作了一種間接通信IPC:通信的雙方不需要顯式指定另一方,而是通過端口進行通信(對應于“信箱”)。程序之間通過端口流通的資料就是消息。

作為一個早期的微核心系統,Mach系統的性能比起當時的宏核心系統(如UNIX)還是存在不小的差距。其中一個原因是Mach為了實作大量的目标,如可裁剪性、可移植性等,導緻其核心複雜,且代碼量較大。不過,Mach的IPC設計仍對後來的很多系統有着非常重大的影響。Mach中端口和消息的設計使得程序間的通信和具體的程序是隔離開的。隻要一個程序擁有某個端口,其就能夠通過這個端口和“另一端”的程序進行通信。後續的微核心系統設計大都考慮了Mach的思想,不管是借鑒其設計還是将其缺陷引以為戒。

L4:圍繞程序間通信優化而設計的微核心系統

根據Mach的經驗,Liedtke等研究人員開始研發L4系列的微核心系統。L4系列微核心系統的一個突出思路是:程序間通信是微核心的核心功能,需要圍繞通信去完成整個系統的設計和實作。L4是當下仍然十分主流的微核心系統,特别是後續衍生出了各種變體和相關的系統。

在L4微核心中,核心隻保留了基本的功能,包括位址空間、線程、程序間通信等,并且不考慮如相容性等要求,而是選擇針對特定硬體做極緻的性能優化。這樣做的好處就是核心的代碼量非常少,可以把少量的功能盡可能支援好。

LRPC:遷移線程模型

遷移線程(thread migration)是一個比較“極端”的優化性能的IPC設計。截止到目前,我們了解到優化IPC性能的大部分工作會關注兩個部分:優化控制流切換的性能和優化資料傳輸的性能。遷移線程認為,其他的IPC設計可以看成将需要處理的資料發送到另一個程序并讓其處理。這也是為什麼控制流切換和資料傳輸會成為主要的瓶頸。如果換一個角度,将另一個程序處理資料的代碼拉到目前程序,那麼我們是不是可以避免控制流的切換(仍然是目前程序處理)以及資料傳輸(資料已經準備在目前程序中)呢?遷移線程就是圍繞這個新的視角進行設計的。

遷移線程方案被用在LRPC、Mach(優化版本)等系統中,是目前純軟體程序間通信優化中效果最好的設計之一。遷移線程的基本原則是:①簡化控制流切換,讓用戶端線程執行“服務端的代碼”;②簡化資料傳輸,共享參數棧和寄存器;③簡化接口,減少序列化等開銷;④優化并發,避免共享的全局資料結構。其中,前兩點原則都基于“将代碼拉到本地”這個新的視角。

遷移線程IPC和主流IPC設計的對比如圖7-13所示。要做到“将代碼拉到本地”,遷移線程首先需要對線程結構進行解耦,明确線程中哪些部分是對通信請求處理起關鍵作用的。然後,這部分允許被調用者(負責處理請求的邏輯)運作在調用者的上下文中,将跨程序調用變成更接近函數調用的形式。

如果使用遷移線程模型,在程序間通信過程中,核心不會阻塞調用者線程,但是會讓調用者線程執行被調用者的代碼。整個過程沒有被調用者線程被喚醒,相反,被調用者端更像是一個“代碼提供者”。此外,核心不會進行完整的上下文切換,而是隻切換位址空間(頁表)等和請求處理相關的系統狀态。其中,不會涉及線程和優先級的切換,也不會調用排程器。遷移線程的優點在于減少了核心排程的時間,并簡化了核心中的IPC處理。在多核場景下,遷移線程方案還可以避免跨核通信引入的開銷。

06 案例分析:Android Binder

繼續閱讀