官术网_书友最值得收藏!

2.1.1 進程間通信

盡管今天的大多數RPC技術已經不再追求這個目標了,但不可否認,RPC出現的最初目的,就是為了讓計算機能夠與調用本地方法一樣去調用遠程方法。所以,我們先來看一下調用本地方法時,計算機是如何處理的。筆者通過以下這段Java風格的偽代碼來定義幾個稍后要用到的概念:


// Caller    :  調用者,代碼里的main()
// Callee    : 被調用者,代碼里的println()
// Call Site : 調用點,即發生方法調用的指令流位置
// Parameter : 參數,由Caller傳遞給Callee的數據,即“hello world”
// Retval    : 返回值,由Callee傳遞給Caller的數據,如果方法能夠正常結束,它是void,
    如果方法異常完成,它是對應的異常
public static void main(String[] args) {
    System.out.println("hello world");
}

在完全不考慮編譯器優化的前提下,程序運行至調用println()方法輸出hello world這行時,計算機(物理機或者虛擬機)要完成以下幾項工作。

1)傳遞方法參數:將字符串hello world的引用地址壓棧。

2)確定方法版本:根據println()方法的簽名,確定其執行版本。這其實并不是一個簡單的過程,無論是編譯時靜態解析,還是運行時動態分派,都必須根據某些語言規范中明確定義的原則,找到明確的Callee,“明確”是指唯一的一個Callee,或者有嚴格優先級的多個Callee,譬如不同的重載版本。筆者曾在《深入理解Java虛擬機》的第8章介紹該過程,有興趣的讀者可以參考,這里不再贅述。

3)執行被調方法:從棧中彈出Parameter的值或引用,并以此為輸入,執行Callee內部的邏輯。這里我們只關心方法是如何調用的,而不關心方法內部具體是如何執行的。

4)返回執行結果:將Callee的執行結果壓棧,并將程序的指令流恢復到Call Site的下一條指令,繼續向下執行。

我們再來考慮如果println()方法不在當前進程的內存地址空間中會發生什么問題。不難想到,這樣會至少面臨兩個直接的障礙。首先,第一步和第四步所做的傳遞參數、傳回結果都依賴于棧內存,如果Caller與Callee分屬不同的進程,就不會擁有相同的棧內存,此時將參數在Caller進程的內存中壓棧,對于Callee進程的執行毫無意義。其次,第二步的方法版本選擇依賴于語言規則,如果Caller與Callee不是同一種語言實現的程序,方法版本選擇就將是一項模糊的不可知行為。

為了簡化討論,我們暫時忽略第二個障礙,假設Caller與Callee是使用同一種語言實現的,先來解決兩個進程之間如何交換數據的問題,這件事情在計算機科學中被稱為“進程間通信”(Inter-Process Communication,IPC)。可以考慮的解決辦法有以下幾種。

·管道(Pipe)或者具名管道(Named Pipe):管道類似于兩個進程間的橋梁,可通過管道在進程間傳遞少量的字符流或字節流。普通管道只用于有親緣關系的進程(由一個進程啟動的另外一個進程)間的通信,具名管道擺脫了普通管道沒有名字的限制,除具有管道的所有功能外,它還允許無親緣關系的進程間的通信。管道典型的應用就是命令行中的“|”操作符,譬如:


ps -ef | grep java

ps與grep都有獨立的進程,以上命令就是通過管道操作符“|”將ps命令的標準輸出連接到grep命令的標準輸入上。

·信號(Signal):信號用于通知目標進程有某種事件發生。除了進程間通信外,進程還可以給進程自身發送信號。信號的典型應用是kill命令,譬如:


kill -9 pid

以上命令即表示由Shell進程向指定PID的進程發送SIGKILL信號。

·信號量(Semaphore):信號量用于在兩個進程之間同步協作手段,它相當于操作系統提供的一個特殊變量,程序可以在上面進行wait()和notify()操作。

·消息隊列(Message Queue):以上三種方式只適合傳遞少量消息,POSIX標準中定義了可用于進程間數據量較多的通信的消息隊列。進程可以向隊列添加消息,被賦予讀權限的進程還可以從隊列消費消息。消息隊列克服了信號承載信息量少、管道只能用于無格式字節流以及緩沖區大小受限等缺點,但實時性相對受限。

·共享內存(Shared Memory):允許多個進程訪問同一塊公共內存空間,這是效率最高的進程間通信形式。原本每個進程的內存地址空間都是相互隔離的,但操作系統提供了讓進程主動創建、映射、分離、控制某一塊內存的程序接口。當一塊內存被多進程共享時,各個進程往往會與其他通信機制,譬如與信號量結合使用,來達到進程間同步及互斥的協調操作。

·本地套接字接口(IPC Socket):消息隊列與共享內存只適合單機多進程間的通信,套接字接口則是更普適的進程間通信機制,可用于不同機器之間的進程通信。套接字(Socket)起初是由UNIX系統的BSD分支開發出來的,現在已經移植到所有主流的操作系統上。出于效率考慮,當僅限于本機進程間通信時,套接字接口是被優化過的,不會經過網絡協議棧,不需要打包拆包、計算校驗和、維護序號和應答等操作,只是簡單地將應用層數據從一個進程復制到另一個進程,這種進程間通信方式即本地套接字接口(UNIX Domain Socket),又叫作IPC Socket。

主站蜘蛛池模板: 织金县| 新乡市| 辽源市| 井研县| 禹州市| 金塔县| 襄樊市| 吉安市| 平顶山市| 乌拉特前旗| 三门县| 永泰县| 清新县| 洞头县| 平乡县| 龙门县| 偃师市| 陈巴尔虎旗| 襄垣县| 临汾市| 北票市| 新建县| 庆阳市| 习水县| 无极县| 元氏县| 房山区| 加查县| 德清县| 贵定县| 安丘市| 滦平县| 洪泽县| 广安市| 封开县| 岑巩县| 阿瓦提县| 巴塘县| 塔河县| 侯马市| 门头沟区|