- 鳳凰架構:構建可靠的大型分布式系統
- 周志明
- 1796字
- 2021-06-24 11:30:51
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。
- Cybersecurity:Attack and Defense Strategies
- 白話區塊鏈
- 嵌入式Linux系統開發:基于Yocto Project
- Linux性能優化
- Windows Phone 8 Application Development Essentials
- AutoCAD 2014中文版從入門到精通
- Delphi Programming Projects
- Linux命令行大全(第2版)
- 操作系統分析
- OpenSolaris設備驅動原理與開發
- Django Project Blueprints
- AWS SysOps Cookbook
- Hands-On GPU Programming with Python and CUDA
- Drupal 7 Cookbook
- Linux操作系統案例教程(第2版)