android binder 機制概述

       首先從概念上來說,什麼是Binder,簡單來說Binder是一種跨進程的通訊方式,從Android Framewok 來說,binder是serviceManager連接各種manger 和相應ManagerService 的橋樑,而binder 可以充當進程的橋樑,它是android ipc 機制中的一種。

       直觀的使用就是aidl,AIDL是Android中IPC(Inter-Process Communication)方式中的一種,AIDL是Android Interface definition language的縮寫(對於小白來說,AIDL的作用是讓你可以在自己的APP裏綁定一個其他APP的service,這樣你的APP可以和其他APP交互。)AIDL只是Android中衆多進程間通訊方式中的一種方式,從另一種角度來講,aidl 其實是對binder 跨進程通訊使用的一種封裝,我們只需要定義一個後綴名爲aidl 的文件,並且定義相關使用的接口,然後通過工具構建,android 會幫助我們在build/generated/aidl_source_output_dir/debug/compileDebugAidl/out/com/example/aidlapplication/IMyAidlInterface.java,創建一個對應的java 文件。aidlapplication 是我的應用名,IMyAidlInterface 是我定義的aidl 文件名,這裏會對應生成一個java接口類,這個類纔是我們進程通訊的關鍵所在,它通過binder 機制來實現進程間的通信的。

     那麼問題來了,android爲什麼要binder 來做進程通信,直接通信不行麼?

大家都知道Android是基於Linux內核的操作系統,那麼一般來說我們的應用當然就是一個linux進程了,linux進程又是相互獨立的,我們叫做進程隔離,這裏主要是出於安全的考慮,每個android 進程app只能運行在自己進程的虛擬地址空間,虛擬地址空間又分爲用戶空間和內核空間,這裏有兩個隔離。

    1.不同進程之間是相互隔離的。

    2.進程內有用戶空間和內核空間的隔離。

所以說進程間是不能直接的通信的,所以就有了binder ,這裏有一點需要注意,那就是每個進程分爲用戶空間和內核空間,第一部分爲“用戶空間”,用來映射其整個進程空間(0x0000 0000-0xBFFF FFFF)即3G字節的虛擬地址;第二部分爲“系統空間”,用來映射(0xC000 0000-0xFFFF FFFF)1G字節的虛擬地址。那是不是n多個進程就會有n多個內核空間和用戶空間呢,其實不是的,用戶空間是有n多個,但是內核空間只有一個或者說是共享的,他是由內核映射的,內核空間是所有進程共享的,而用戶空間是不能共享的,所以內核空間纔會成爲多個進程數據傳輸的一個橋樑。那麼我們所說的binder進程間通信主要就是共享內核空間,用戶進程通過用戶地址空間來訪問這塊內核緩衝區的內容,而binder 驅動使用內核空間地址來訪問這塊緩衝區的內容,這是binder 進程間通訊的關鍵所在。

那他們到底是如何通信的呢,我們假設進程A爲客戶端,B 爲服務端,那麼B 要先在serviceManager(以後簡稱smg) 中註冊,通過註冊,smg獲取到b中的binder 對象的引用,並且使用一個單鏈表將它保存起來。現在A 要和B進行通信,首先通過綁定B中註冊的services ,獲得一個iBinder 接口對象,這個過程實際就是A通過binder 驅動 獲取到一個binder代理BinderProxy對像,A通過代理對象BinderProxy  調用接口方法,BinderProxy對象再通過binder 驅動,從serviceManager 中獲取binder引用,然後調用binder 所定義的接口方法,從而達到進程間通信的,在整個調用的過程中是同步進行的。

 這裏主要有三個方面,服務端的註冊和serviceManager 的註冊,客戶端的綁定

1.服務端的註冊

   server註冊binder 實體信息到smg 的時候請求數據需要該binder 實體信息的描述信息,這個在之後進行查詢的時候smg根據描述信息來獲取對應的binder 引用編號,每個通過binder 通信的進程都要打開binder 驅動一次(至多一次)server也不例外,打開binder 驅動後,內核會調用驅動程序的binder_open方法,該方法內部將會創建binder_proc 結構體,並存儲了進程信息及uid信息,這也是binder通信安全的一個原因,server 的註冊是爲了後邊,客戶端與其通信時能夠順利建立連接(拿到客戶端binder 代理對象),從而實現跨進程通信。

 

2.serviceManager 的註冊

       serviceManager 的註冊過程大致分五步

1.打開binder 驅動

2.將自己註冊爲binder 驅動 的大管家(其他進程根據引用編號0可以找到smg 對應的binder 實體)

3.進入循環不斷從binder 驅動 中讀取消息(無消息時被阻塞)

4.讀取到消息後處理消息

5.不斷循環,永不退出

serviceManager 通過binder_open 打開驅動,將自己註冊爲驅動的大管家,binder驅動創建servicemanager 的binder 實體對象引用編號爲0,並且所有的客戶端都知道,smg進入無限循環狀態,不斷從binder 驅動中讀取消息,讀取到消息後,根據消息的描述信息返回對應的binder 對象引用。

在這個過程中出現了 Ibinder , IInterface ,Binder, BinderProxy ,stub,servicesManager,  binder 驅動 , 一些關類或者工具。

1.IBinder 是一個接口,它代表一種跨進程傳輸的能力,實現這個接口,就能將這個對象進行跨進程的傳遞,它是遠程對象的基本接口,是爲高性能而設計的的輕量級遠程調用機制的核心部分(爲啥說是高性能這個後邊會講到),但是它不僅用於遠程調用,也用於進程內部調用,這個接口還定義了與遠程對象交互的協議,但是使用的時候不要直接實現這個接口,而因該從Binder派生,關鍵方法transact()。

2.IInterface 接口,是clienter 端和server 端的調用契約,實現這個接口就代表遠程server對象具有什麼能力,因爲IInterface 接口的asBinder方法的實現可以將Binder 本地對象或者代理對象進行返回。

3.Binder 對象,binder 機制中進程間通訊的對象,對於service 端爲Binder本地對象,對於client端爲Binder代理對象BinderProxy,是一個可以跨進程引用的對象,它的實體位於進程中,而他的引用卻遍佈系統的的各個進程中,這個引用和java裏的引用一樣,即可以是強引用也可以是弱引用,可以從一個進程傳給其他進程,讓大家訪問同一個server,就像一個對象引用負值給另一個對象一樣,是serviceManager連接各個manager 和相應ManagerService的橋樑,而binder 可以充當兩個進程的橋樑,關鍵方法onTransact()。

4.BinderPraxy 類:是binder 類的內部類,它代表進程的Binder對像的本地代理,因繼承自IBinde,因而具有跨進程傳輸的能力,在跨進程時,Binder 驅動會自動完成兩個對象的轉換。

5.stub:AIDL的時候編工具給我們生成的一個名爲stub 的靜態內部類,這個類繼承自Binder ,說明他是一個本地Binder 本地對象,它實現了IInterface 接口 ,表明它具有server 承諾給clente的能力。stub是一個抽象類具體IInterface 要開發者自己實現,這個名字是工具自動給訂的,我們可以自己修改,但是要保證兩端一致。

6.ServicesManager 運行在一個單獨的進程中,他集中管理系統內的所有的服務,通過權限控制進程是否有權註冊服務,通過字符串名稱來查找對應的service,在binder 進程通信機制中扮演着上下文管理的角色,他的核心工作就是註冊和查詢服務,註冊時他會存儲當前service 的binder 對象引用,到一個單鏈表中。

7.Binder 驅動:它是屬於內核空間的,屬於整個通信的核心,雖然叫驅動,但是實際上和硬件沒太大關係,只是實現方式和驅動差不多,Binder 機制中進程間通訊的介質,binder驅動會對具有跨進程傳輸能力的對象做特殊處理,自動完成代理對象和本地對象的轉換,serviceManager啓動後將打開binder 驅動,並將自己註冊爲binder 驅動的管家,其他進程根據引用編號0可以找到smg(servicemanager)對應的binder 實體,從而和serviceManage建立通信,servicemanager 返回服務端的binder引用。

這是binder 機制通信中的幾個重要角色,從整體上Binder 框架定義了四個角色:server ,client,servicesManager,以及Binder 驅動。

server 服務端,運行在一個單獨進程的用戶空間

client  客戶端,同樣運行在一個單獨的進程用戶空間中

servicesManager 一個服務的管理進程,同樣運行在一個單獨進程的用戶空間中

binder 驅動程序 運行在內核空間中.

通過上面的理解,我們可以這樣理解binder 通信的整個過程

binder分配的默認的內存大小爲1M- 8k,普通應用最大傳輸爲

(1*1024*1024) - (4096 *2)

約1016kb不到1M ,默認的最大可併發訪問的線程數爲16,當然我們一般使用中也不會超過這個限制,超出會報錯,最常見的就是intent 傳值。

binder 其實非常複雜,他在應用層,frameWork 層 native jni 及內核層都有實現,他貫穿了整個androd 系統架構,是整個android進程間通信的橋樑,大到我們所說的進程間數據通信,小到兩個activity頁面的啓動,都有用到binder 通信。

基本概念及原理清楚了,那麼我上面說binder 是一種高效的進程間通信,到底是如何高效呢?

在這之前我們先大概瞭解下進程間通行的幾種方式,pipe管道,消息隊列,socket,共享內存,binder

   1.binder android 特有的跨進程通信,需要一次數據拷貝

  2. pipe ,消息隊列,socket 都需要將數據拷貝兩次

  3.共享內存,標準的linux ipc 機制,需要0次數據拷貝

binder 內存管理機制

 主要原理是接收端的虛擬進程地址空間和虛擬內核地址空間,都映射到同一塊物理內存空間中,當client端與server 端發送消息時,client作爲數據發送端,先從自己的進程空間把ipc 通信數據拷貝到內核空間,而server 作爲數據的的接收端與內核共享數據,不再需要拷貝數據,而是通過內存地址空間的偏移量即可獲取內存地址,整個過程只發生一次內存拷貝。這就是他的高效所在。

  那直接將數據的發送端的數據和數據的接收端直接映射到一塊物理空間,豈不是更加高效,這與標準linux 標準的共享內存的IPC 機制,就沒有區別了,使用共享內存只需要0次拷貝那不更加高效,但是對於多進程的同步問題比較複雜,android 出於基本速率和安全性的考慮選擇使用binder 進程跨進程通信。所以說處於綜合考慮說binder 是一種高效的進程間通信方式。

 

到此android binder 機制概述 就完了,關於binder 牽扯麪比較廣,也比較複雜,爲此我也是準備和學習了兩個星期纔將文章寫完,中間險些流產了,這些完全是我自己對於binder 的學習和理解,如果有那塊出現理解偏差歡迎一起來探討。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章