Binder概述
Linux的進程間通信機制
Android系統中,每一個應用程序都是由一些Activity和Service組成的,這些Activity和Service有可能運行在同一個進程中,也有可能運行在不同的進程中。那么,不在同一個進程的Activity或者Service是如何通信的呢?這就是本文中要介紹的Binder進程間通信機制了。
我們知道,Android系統是基于Linux內核的,而Linux內核繼承和兼容了豐富的Unix系統進程間通信(IPC)機制。有傳統的管道(Pipe)、信號(Signal)和跟蹤(Trace),這三項通信手段只能用于父進程與子進程之間,或者兄弟進程之間;后來又增加了命令管道(Named Pipe),使得進程間通信不再局限于父子進程或者兄弟進程之間;為了更好地支持商業應用中的事務處理,在AT&T的Unix系統V中,又增加了三種稱為“System V IPC”的進程間通信機制,分別是報文隊列(Message)、共享內存(Share Memory)和信號量(Semaphore);后來BSD Unix對“System V IPC”機制進行了重要的擴充,提供了一種稱為插口(Socket)的進程間通信機制。若想進一步詳細了解這些進程間通信機制,建議參考羅升陽老師的Android學習啟動篇一文中提到《Linux內核源代碼情景分析》一書。
各個進程間通信方式比較:
- 管道:在創建時分配一個page大小的內存,緩存區大小比較有限;
- 消息隊列:信息復制兩次,額外的CPU消耗;不合適頻繁或信息量大的通信;
- 共享內存:無須復制,共享緩沖區直接付附加到進程虛擬地址空間,速度快;但進程間的同步問題操作系統無法實現,必須各進程利用同步工具解決;
- 套接字:作為更通用的接口,傳輸效率低,主要用于不同機器或跨網絡的通信;
- 信號量:常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作為進程間以及同一進程內不同線程之間的同步手段。
- 信號: 不適用于信息交換,更適用于進程中斷控制,比如非法內存訪問,殺死某個進程等;
Binder的優勢
- 從性能的角度
數據拷貝次數:Binder數據拷貝只需要一次,而管道、消息隊列、Socket都需要2次,但共享內存方式一次內存拷貝都不需要;從性能角度看,Binder性能僅次于共享內存。
- 從穩定性的角度
Binder是基于C/S架構的,C/S架構是指客戶端(Client)和服務端(Server)組成的架構,Client端有什么需求,直接發送給Server端去完成,架構清晰明朗,Server端與Client端相對獨立,穩定性較好;而共享內存實現方式復雜,沒有客戶與服務端之別, 需要充分考慮到訪問臨界資源的并發同步問題,否則可能會出現死鎖等問題;從這穩定性角度看,Binder架構優越于共享內存。
- 從安全的角度
傳統Linux IPC的接收方無法獲得對方進程可靠的UID/PID,從而無法鑒別對方身份;而Android作為一個開放的開源體系,擁有非常多的開發平臺,App來源甚廣,因此手機的安全顯得額外重要;對于普通用戶,絕不希望從App商店下載偷窺隱射數據、后臺造成手機耗電等等問題,傳統Linux IPC無任何保護措施,完全由上層協議來確保。Android為每個安裝好的應用程序分配了自己的UID,故進程的UID是鑒別進程身份的重要標志,前面提到C/S架構,Android系統中對外只暴露Client端,Client端將任務發送給Server端,Server端會根據權限控制策略,判斷UID/PID是否滿足訪問權限,目前權限控制很多時候是通過彈出權限詢問對話框,讓用戶選擇是否運行。Android 6.0,也稱為Android M,在6.0之前的系統是在App第一次安裝時,會將整個App所涉及的所有權限一次詢問,只要留意看會發現很多App根本用不上通信錄和短信,但在這一次性權限權限時會包含進去,讓用戶拒絕不得,因為拒絕后App無法正常使用,而一旦授權后,應用便可以胡作非為。針對這個問題,google在Android M做了調整,不再是安裝時一并詢問所有權限,而是在App運行過程中,需要哪個權限再彈框詢問用戶是否給相應的權限,對權限做了更細地控制,讓用戶有了更多的可控性,但同時也帶來了另一個用戶詬病的地方,那也就是權限詢問的彈框的次數大幅度增多。對于Android M平臺上,有些App開發者可能會寫出讓手機異常頻繁彈框的App,企圖直到用戶授權為止,這對用戶來說是不能忍的,用戶最后吐槽的可不光是App,還有Android系統以及手機廠商,有些用戶可能就跳果粉了,這還需要廣大Android開發者以及手機廠商共同努力,共同打造安全與體驗俱佳的Android手機。
傳統IPC只能由用戶在數據包里填入UID/PID;另外,可靠的身份標記只有由IPC機制本身在內核中添加。其次傳統IPC訪問接入點是開放的,無法建立私有通道。從安全角度,Binder的安全性更高。
- 從語言層面的角度
大家多知道Linux是基于C語言(面向過程的語言),而Android是基于Java語言(面向對象的語句),而對于Binder恰恰也符合面向對象的思想,將進程間通信轉化為通過對某個Binder對象的引用調用該對象的方法,而其獨特之處在于Binder對象是一個可以跨進程引用的對象,它的實體位于一個進程中,而它的引用卻遍布于系統的各個進程之中。可以從一個進程傳給其它進程,讓大家都能訪問同一Server,就像將一個對象或引用賦值給另一個引用一樣。Binder模糊了進程邊界,淡化了進程間通信過程,整個系統仿佛運行于同一個面向對象的程序之中。
從語言層面,Binder更適合基于面向對象語言的Android系統,對于Linux系統可能會有點“水土不服”。另外,Binder是為Android這類系統而生,而并非Linux社區沒有想到Binder IPC機制的存在。也并非Linux現有的IPC機制不夠好,相反地,經過這么多優秀工程師的不斷打磨,依然非常優秀,每種Linux的IPC機制都有存在的價值,同時在Android系統中也依然采用了大量Linux現有的IPC機制,根據每類IPC的原理特性,因時制宜,不同場景特性往往會采用其下最適宜的。比如在Android OS中的Zygote進程的IPC采用的是Socket(套接字)機制,Android中的Kill Process采用的signal(信號)機制等等。而Binder更多則用在system_server進程與上層App層的IPC交互。
Binder的缺點
Binder對CPU和內存的需求比較低,效率比較高,從而進一步說明Binder適合于移動系統Android,但是,也有一定缺點,就是不同利用Binder輸出大數據,比如利用Binder傳輸幾M大小的圖片,便會出現異常,雖然有廠商會增加Binder內存,但是也不可能比系統默認內存大很多,否則整個系統的可用內存大幅度降低。
講了Binder的這么多好處,就來看看Binder是如何運行起來的吧!
學習計劃
完成一次Client到Server通信的流轉還是比較復雜的,從應用層到Framework層、從UserSpace到KernelSpace、從Java層到Jni層都涉及到了。Binder學習的這個過程也可以對Android、Linux系統有更深入的了解。
廢話就不多說了,回到正題。個人先簡單總結了幾大主要步驟:
- ServiceManager啟動,注冊成為Binder通信的上下文管理者
- 如何獲取Service Manager
- Server啟動并向Service Manager注冊
- Client通過ServiceManager獲取Service信息
- Client根據得到的Service信息建立與Service所在的Server進程通信的通路,然后直接與Service交互。
這個順序是由底層向上層排的,但為了方便學習,我會從上層入手,逐步深入的學習。