最近想學些Android Framework中的東西,必經之路就是binder,不夸張的說,binder是整個android架構 的基礎。本文從Binder的基本概念和框架入手,講述binder機制的點點滴滴。全文分為以下四部分:1、Android為何設計binder進行進程間通信。2、Binder的框架結構和概述。3、Client和Server通信過程。4、binder總結。
PS,本文絕大部分內容來自網絡,是我自己學習binder的一篇總結,而且本文只是binder的入門,不涉及到過多底層的描述,要想更多了解binder的內容,還請移步到引用文獻中老羅和鄧凡平老師的文章。
Android IPC 為何設計Binder
Android 系統中,為了向應用開發者提供豐富多樣的功能,這種通信方式更是無處不在,諸如媒體播放,音頻視頻捕獲,通知,到各種讓手機更智能的傳感器(加速度,方位,溫度,光亮度等)都由不同的Server負責管理,應用程序只需做為Client與這些Server建立連接便可以使用這些服務,花很少的時間和精力就能開發出令人眩目的功能。
設計Binder的原因主要有以下3個:
- Linux支持CS通信模式的IPC少 目前linux支持的IPC包括傳統的管道,System V IPC,即消息隊列/共享內存/信號量,以及socket,其中只有socket支持Client-Server的通信方式。當然也可以在這些底層機制上架設一套協議來實現Client-Server通信,但這樣增加了系統的復雜性,在嵌入式這種條件復雜,資源稀缺的系統中可靠性也難以保證。
- 傳輸性能低 socket作為一款通用接口,其傳輸效率低,開銷大,主要用在跨網絡的進程間通信和本機上進程間的低速通信。消息隊列和管道采用存儲-轉發方式,即數據先從發送方緩存區拷貝到內核開辟的緩存區中,然后再從內核緩存區拷貝到接收方緩存區,至少有兩次拷貝過程。共享內存雖然無需拷貝,但控制復雜,難以使用。
- 安全性不高 Android作為一個開放式,擁有眾多開發者的的平臺,應用程序的來源廣泛,確保智能終端的安全是非常重要的。終端用戶不希望從網上下載的程序在不知情的情況下偷窺隱私數據,連接無線網絡,長期操作底層設備導致電池很快耗盡等等。傳統IPC沒有任何安全措施,完全依賴上層協議來確保。首先傳統IPC的接收方無法獲得對方進程可靠的UID/PID(用戶ID/進程ID),從而無法鑒別對方身份。
Binder總體概述
Binder框架及概述
- Binder是Android中進程間通信最常見的方式,采用Client-Server的通信模型。
- Client和Server之間的通信通過Binder驅動間接實現。
-
Service Manager是一個守護進程,用來管理Server,并向Client提供查詢Server接口的能力。
binder框架
四大角色
- Binder Driver:Binder驅動,跑在內核空間,由framework實現。
- Service Manager:Services管理員,跑在用戶空間,由framework實現。
- Server: 服務端,跑在用戶空間,framework實現了很多系統級的server,也可以在application中實現自己的server。
- Client:客戶端,跑在用戶空間,一般在application中實現。
通訊模型
這四個角色的關系和互聯網類似:Server是服務器,Client是客戶終端,ServiceManager是域名服務器(DNS),Binder驅動是路由器。
(1)客戶端通過某種方式得到服務器端的代理對象。代理對象和他的本地對象沒有什么差別,它可以像其他本地對象一樣調用其方法,訪問其變量。
(2)客戶端通過調用代理對象的方法向服務器端發送請求信息。
(3)代理對象通過binder設備節點(/dev/binder),把用戶請求信息發送到Linux內核空間(內存共享),由Binder驅動獲取并發送到服務進程。
(4)服務進程處理用戶請求,并通過Linux內核的Binder驅動返回處理結果給客戶端的代理對象。
(5)客戶端收到服務端的返回結果。
四大角色的分工
-
Binder Driver:Binder驅動雖然默默無聞,卻是通信的核心。
盡管名叫‘驅動’,實際上和硬件設備沒有任何關系,只是實現方式和設備驅動程序是一樣的。驅動和應用程序之間定義了一套接口協議,主要功能由open()、ioctl()接口實現。 -
ServiceManager:相當于DNS,為服務端的地址取一個好記的名字,便于客戶端訪問。
有一個有趣的事情,ServiceManager是一個進程,Server在另一個進程,Server向ServiceManager注冊一個好記的名字必然會涉及進程間通信。當前實現的是進程間通信卻又要用到進程間通信,這就好象蛋可以孵出雞,前提卻是要找只雞來孵蛋。Binder的實現比較巧妙:預先創造一只‘0號雞’來孵蛋:ServiceManager和其它進程同樣采用Binder通信,這里ServiceManager是Server端,有自己的Binder對象(實體),其它Server和Client進程在這里都是Client,需要通過這個Binder的引用來實現Binder的注冊,查詢和獲取。ServiceManager提供的Binder比較特殊,它沒有名字也不需要注冊,這個Binder的引用在所有Client中都固定為0而無須通過其它手段獲得。類比網絡通信,0號引用就好比域名服務器的地址,你必須預先手工或動態配置好。要注意這里說的Client是相對SServiceManager而言的,它本身可能是個提供服務的Server,但對ServiceManager來說它仍然是個Client。 -
Server:提供各種各樣的服務
Server利用保留的0號引用向ServiceManager注冊了Binder實體及其名字,ServiceManager收到后,將‘張三’和Binder引用填入到一張查找表中。Client就可以通過名字獲得該Binder的引用了。 -
Client:獲取Server的服務
Client也利用保留的0號引用向ServiceManager請求訪問某個Binder:我申請獲得名字叫‘張三’的Binder的引用。ServiceManager收到這個連接請求,從請求數據包里獲得Binder的名字,在查找表里找到該名字對應的條目,從條目中取出Binder的引用,將該引用作為回復發送給發起請求的Client。 -
代理對象:是指在客戶端應用程序中獲取生成的Server代理(proxy)類對象。
從應用程序角度看代理對象和本地對象沒有差別,都可以調用其方法,方法都是同步的,并且返回相應的結果。 -
匿名Binder:并不是所有Binder都需要注冊給ServiceManager廣而告之的。
Server端可以通過已經建立的Binder連接將創建的Binder實體傳給Client,當然這條已經建立的Binder連接必須是通過實名Binder實現。由于這個Binder沒有向ServiceManager注冊名字,所以是個匿名Binder。Client將會收到這個匿名Binder的引用,通過這個引用向位于Server中的實體發送請求。匿名Binder為通信雙方建立一條私密通道,只要Server沒有把匿名Binder發給別的進程,別的進程就無法通過窮舉或猜測等任何方式獲得該Binder的引用,向該Binder發送請求。
從面向對象的角度,這個Binder對象現在有了兩個引用:一個位于ServiceManager中,一個位于發起請求的Client中。如果接下來有更多的Client請求該Binder,系統中就會有更多的引用指向該Binder,就象java里一個對象存在多個引用一樣。而且類似的這些指向Binder的引用是強類型,從而確保只要有引用Binder實體就不會被釋放掉。
Client和Server的通訊過程
前戲
-
獲得ProcessState對象:一個進程會創建一個ProcessState單例對象,每個進程只能創建一個,作用是維護管理當前進程中所有服務代理對象(Service proxy)。具體代碼:sp proc(ProcessState::self());
在ProcessState中打開了一個binder設備文件,并返回一個fd(文件描述符),這個fd很重要,用來找到要通信的那個binder對象。 - 獲得ServiceManager的代理對象:ServiceManager是服務管家,它負責系統服務的管理。Client是通過這個代理對象和Service Manager通訊的。具體代碼:sp sm = defaultServiceManager(); 這個對象的handle為0,也就是說和ServiceManager通信都使用handle為0的binder。
- 真正傳輸的對象IPCThreadState:IPCThreadState也是一個singleton的類型,一個進程中也只能有一個這樣的對象。它在android的binder機制中扮演什么樣的角色呢?IPCThreadState主要負責信息的傳遞,所有要到達binder驅動和驅動中出來的信息,都要由IPCThreadState來傳遞。不管是Client進程和Service進程都是需要用IPCThreadState來和binder設備通訊。
Server初始化(MediaService為例)
Server初始化的代碼如下:
//獲得一個ProcessState實例
sp proc(ProcessState::self());
//得到一個ServiceManager的代理對象
sp sm = defaultServiceManager();
//初始化MediaPlayerService服務,并向服務管家成功添加了這些服務。
MediaPlayerService::instantiate();
//啟動Process的線程池,進入循環狀態,以便接收來自客戶端的請求
ProcessState::self()->startThreadPool();
//將IPCThreadState對象加入到剛才的線程池
IPCThreadState::self()->joinThreadPool();
作為Service進程,當他完成初始化工作之后,需要進入循環狀態等待客戶端的請求,Service進程調用它的IPCThreadState對象的joinThreadPool方法,開始輪詢binder設備,等待客戶端請求的到來。
MediaService通信
Binder總結
如果一個服務需要通過binder機制對外提供跨進程的接口,需要做下面這些事情。
(1)第一步,需要為這個接口定義一個繼承自IInterface的接口類,假設叫做IMyService。
(2)第二步,需要定義兩個binder類,一個是代理類BpMyService,繼承BpInterface;另一個是實現類BnMyService,繼承BnInterface。
(3)第三步,定義BnMyService的子類,這個子類可以是任何名字,比如就叫MyService,在其中真正實現接口所提供的各個函數。
(4)第四步,創建MyService的實例,注冊到服務管理器,也可以在其它接口的函數中創建。
這篇基本是Binder機制的入門介紹,以后有機會詳細研究一下Binder機制。