Android跨進程通信IPC整體內(nèi)容如下
- 1、Android跨進程通信IPC之1——Linux基礎(chǔ)
- 2、Android跨進程通信IPC之2——Bionic
- 3、Android跨進程通信IPC之3——關(guān)于"JNI"的那些事
- 4、Android跨進程通信IPC之4——AndroidIPC基礎(chǔ)1
- 4、Android跨進程通信IPC之4——AndroidIPC基礎(chǔ)2
- 5、Android跨進程通信IPC之5——Binder的三大接口
- 6、Android跨進程通信IPC之6——Binder框架
- 7、Android跨進程通信IPC之7——Binder相關(guān)結(jié)構(gòu)體簡介
- 8、Android跨進程通信IPC之8——Binder驅(qū)動
- 9、Android跨進程通信IPC之9——Binder之Framework層C++篇1
- 9、Android跨進程通信IPC之9——Binder之Framework層C++篇2
- 10、Android跨進程通信IPC之10——Binder之Framework層Java篇
- 11、Android跨進程通信IPC之11——AIDL
- 12、Android跨進程通信IPC之12——Binder補充
- 13、Android跨進程通信IPC之13——Binder總結(jié)
- 14、Android跨進程通信IPC之14——其他IPC方式
- 15、Android跨進程通信IPC之15——感謝
本文的主要內(nèi)容如下:
- 1 Android整體架構(gòu)
- 2 IPC原理
- 3 Binder簡介
- 4 Binder通信機制
- 5 Binder協(xié)議
一、Android整體架構(gòu)
(一) Android的整體架構(gòu)
為了讓大家更好的理解Binder機制,我們先來看下Android的整體架構(gòu)。因為這樣大家就知道在Android架構(gòu)中Binder出于什么地位。
用一下官網(wǎng)上的圖片

從下往上依次為:
- 內(nèi)核層:Linux內(nèi)核和各類硬件設(shè)備的驅(qū)動,這里需要注意的是,Binder IPC驅(qū)動也是這一層的實現(xiàn),比較特殊。
- 硬件抽象層:封裝"內(nèi)核層"硬件驅(qū)動,提供可供"系統(tǒng)服務(wù)層"調(diào)用的統(tǒng)一硬件接口
- 系統(tǒng)服務(wù)層:提供核心服務(wù),并且提供可供"應(yīng)用程序框架層"調(diào)用的接口
- Binder IPC 層:作為"系統(tǒng)服務(wù)層"與"應(yīng)用程序框架層"的IPC橋梁,相互傳遞接口調(diào)用的數(shù)據(jù),實現(xiàn)跨進程的通信。
- 應(yīng)用程序框架層:這一層可以理解為Android SDK,提供四大組件,View繪制等平時開發(fā)中用到的基礎(chǔ)部件
(二) Android的架構(gòu)解析
在一個大的項目里面,分層是非常重要的,處于最底層的接口最具有"通用性",接口顆粒度最細,越往上層通用性降低。理論上來說上面每一層都可以"開放"給開發(fā)者調(diào)用,例如開發(fā)者可以直接調(diào)用硬件抽象層的接口去操作硬件,或者直接調(diào)用系統(tǒng)服務(wù)層中的接口去直接操作系統(tǒng)服務(wù),甚至像Windows開發(fā)一樣,開發(fā)者可以在內(nèi)核層寫程序,運行在內(nèi)核中。不過開放帶來的問題就是開發(fā)者權(quán)利太大,對于系統(tǒng)的穩(wěn)定性是沒有任何好處的,一個病毒制作者搞一個內(nèi)核層的病毒出來,系統(tǒng)可能就永遠起不來。所以谷歌的做法是將開發(fā)者的權(quán)利收攏到"應(yīng)用程序框架層",開發(fā)者只能調(diào)用這一層的接口。
在上面的層次中,內(nèi)核層與硬件抽象層均用C/C++實現(xiàn),系統(tǒng)服務(wù)層是以Java實現(xiàn),硬件抽象層編譯為so文件,以JNI的形式供系統(tǒng)服務(wù)層使用。系統(tǒng)服務(wù)層中的服務(wù)隨著系統(tǒng)啟動而啟動,只要不關(guān)機,就會一直運行。這些服務(wù)干什么事情?其實很簡單,就是完成一個手機有的核心功能如短信的收發(fā)管、電話的接聽、掛斷以及應(yīng)用程序的包管理、Activity的管理等等。每一個服務(wù)均運行在一個獨立的進程中,因此是以Java實現(xiàn),所以本質(zhì)上來說是運行在一個獨立進程的Dalvik虛擬機中。那么問題來了,開發(fā)者的App也運行在一個獨立的進程空間中,如果調(diào)用到系統(tǒng)的服務(wù)層中的接口?答案是IPC(Inter-Process Communication),進程間通訊是和RPC(Remote Procedure Call)不一樣的,實現(xiàn)原理也不一樣。每一個系統(tǒng)服務(wù)在應(yīng)用框架層都有一個Manager與之對應(yīng),方便開發(fā)者調(diào)用其相關(guān)功能,具體關(guān)系如下:

(三)、總結(jié)一下:
- Android 從下而上分了內(nèi)核層、硬件抽象層、系統(tǒng)服務(wù)層、Binder IPC 層、應(yīng)用程序框架層
- Android 中"應(yīng)用程序框架層"以 SDK 的形式開放給開發(fā)者使用,"系統(tǒng)服務(wù)層" 中的核心服務(wù)隨系統(tǒng)啟動而運行,通過應(yīng)用層序框架層提供的 Manager 實時為應(yīng)用程序提供服務(wù)調(diào)用。系統(tǒng)服務(wù)層中每一個服務(wù)運行在自己獨立的進程空間中,應(yīng)用程序框架層中的 Manager 通過 Binder IPC 的方式調(diào)用系統(tǒng)服務(wù)層中的服務(wù)。
二、IPC原理
從進程的角度來看IPC機制

每個Android進程,只能運行在自己的進程所擁有的虛擬地址空間,如果是32位的系統(tǒng),對應(yīng)一個4GB的虛擬地址空間,其中3GB是用戶空,1GB是內(nèi)核空間,而內(nèi)核空間的大小是可以通過參數(shù)配置的。對于用戶空間,不同進程之間彼此是不能共享的,而內(nèi)核空間確實可以共享的。Client進程與Server進程通信,恰恰是利用進程間可共享的內(nèi)核內(nèi)空間來完成底層通信工作的,Client端與Server端進程往往采用ioctl等方法跟內(nèi)核空間的驅(qū)動進行。
三、Binder綜述
(一) Binder簡介
1、Binder的由來
簡單的說,Binder是Android平臺上的一種跨進程通信技術(shù)。該技術(shù)最早并不是谷歌公司提出的,它前身是Be Inc公司開發(fā)的OpenBinder,而且在Palm中也有應(yīng)用。后來OpenBinder的作者Dianne Hackborn加入了谷歌公司,并負責(zé)Android平臺開發(fā)的工作,所以把這項技術(shù)也帶進了Android。
我們知道,在Android的應(yīng)用層次上,基本上已經(jīng)沒有了進程的概念了,然后在具體的實現(xiàn)層次上,它畢竟還是要構(gòu)建一個個進程之上的。實際上,在Android內(nèi)部,哪些支持應(yīng)用的組件往往會身處不同的繼承,那么應(yīng)用的底層必然會涉及大批的跨進程通信,為了保證了通信的高效性,Android提供了Binder機制。
2、什么是Binder
讓我們從四個維度來看Binder,這樣會讓大家對理解Binder機制更有幫助
- 1 從來類的角度來說,Binder就是Android的一個類,它繼承了IBinder接口
- 2 從IPC的角度來說,Binder是Android中的一個中的一種跨進程通信方式,Binder還可以理解為一種虛擬的物理設(shè)備,它的設(shè)備驅(qū)動是/dev/binder,該通信方式在Linux中沒有(由于耦合性太強,而Linux沒有接納)
- 3 從Android Framework角度來說,Binder是ServiceManager連接各種Manager(ActivityManager、WindowManager等)和相應(yīng)的ManagerService的橋梁
- 4 從Android應(yīng)用層的角度來說,Binder是客戶端和服務(wù)端進行通信的媒介,當你bindService的時候,服務(wù)端會返回一個包含了服務(wù)端業(yè)務(wù)調(diào)用的Binder對象,通過這個Binder對象,客戶端就可以獲取服務(wù)端提供的服務(wù)或者數(shù)據(jù),這里的服務(wù)包括普通服務(wù)和基于AIDL的服務(wù)。
3、Binder機制的意義
Binder機制具有兩層含義:
- 1 是一種跨進程通信的方式(IPC)
- 2 是一種遠程過程調(diào)用方式(PRC)
而從實現(xiàn)的角度來說,Binder核心被實現(xiàn)成一個Linux驅(qū)動程序,并運行于內(nèi)核態(tài)。這樣它才能具有強大的跨進程訪問能力。
4、和傳統(tǒng)IPC機制相比,谷歌為什么采用Binder
我們先看下Linux中的IPC通信機制:
- 1、傳統(tǒng)IPC:匿名管道(PIPE)、信號(signal)、有名管道(FIFO)
- 2、AT&T Unix:共享內(nèi)存,信號量,消息隊列
- 3、BSD Unix:Socket
關(guān)于這塊如果大家不了解,請看前面的文章。
雖然Android繼承Linux內(nèi)核,但是Linux與Android通信機制是不同的。Android中有大量的C/S(Client/Server)應(yīng)用方式,這就要求Android內(nèi)部提供IPC方法,而如果采用Linux所支持的進程通信方式有兩個問題:性能和安全性。那
- 性能:目前Linux支持的IPC包括傳統(tǒng)的管道,System V IPC(包括消息隊列/共享內(nèi)存/信號量)以及socket,但是只有socket支持Client/Server的通信方式,由于socket是一套通用當初網(wǎng)絡(luò)通信方式,其效率低下,且消耗比較大(socket建立連接過程和中斷連接過程都有一定的開銷),明顯在手機上不適合大面積使用socket。而消息隊列和管道采用"存儲-轉(zhuǎn)發(fā)" 方式,即數(shù)據(jù)先從發(fā)送方緩存區(qū)拷貝到內(nèi)核開辟的緩存區(qū)中,然后再從內(nèi)核緩存中拷貝到接收方緩存中,至少有兩次拷貝過程。共享內(nèi)存雖然無需拷貝,但控制復(fù)雜,難以使用。
- 安全性:在安全性方面,Android作為一個開放式,擁有眾多開發(fā)者的平臺,應(yīng)用程序的來源廣泛,確保智能終端的安全是非常重要的。終端用戶不希望從網(wǎng)上下載的程序在不知情的情況下偷窺隱私數(shù)據(jù),連接無線網(wǎng)絡(luò),長期操作底層設(shè)備導(dǎo)致電池很快耗盡的情況。傳統(tǒng)IPC沒有任何安全措施,完全依賴上層協(xié)議來去報。首先傳統(tǒng)IPC的接受方無法獲取對方進程可靠的UID/PID(用戶ID/進程ID),從而無法鑒別對方身份。Android為每個安裝好的應(yīng)用程序分配了自己的UID,故進程的UID是鑒別進程的身份的重要標志。使用傳統(tǒng)IPC只能由用戶在數(shù)據(jù)包里填入UID/PID,但這樣不可靠,容易被惡意程序利用。可靠的身份標記只由IPC機制本身在內(nèi)核中添加。其次傳統(tǒng)IPC訪問接入點是開放的,無法建立私有通道。比如命名管道、system V的鍵值,socket的ip地址或者文件名都是開放的,只要知道這些接入點的程序都可以對端建立連接,不管怎樣都無法阻止惡意程序通過接收方地址獲得連接。
基于以上原因,Android需要建立一套新的IPC機制來滿足系統(tǒng)對通信方式,傳輸性能和安全性的要求,所以就有了Binder。Binder基于Client/Server通信模式,傳輸過程只需要一次拷貝,為發(fā)送發(fā)添加UID/PID身份,雞翅實名Binder也支持匿名Binder,安全性高。下圖為Binder通信過程示例:

- 相比于傳統(tǒng)的跨進程通信手段,通信雙方必須要處理線程同步,內(nèi)存管理等問題,工作量大,而且問題多,就像我們前面介紹的傳統(tǒng)IPC 命名管道(FIFO) 信號量(semaphore) 消息隊列已經(jīng)從Android中去掉了,同其他IPC相比,Socket是一種比較成熟的通信手段了,同步控制也很容易實現(xiàn)。Socket用于網(wǎng)絡(luò)通信非常合適,但是用于進程間通信就效率很低。
- Android在架構(gòu)上一直希望模糊進程的概念,取而代之以組件的概念。應(yīng)用也不需要關(guān)心組件存放的位置、組件運行在那個進程中、組件的生命周期等問題。隨時隨地的,只要擁有Binder對象,就能使用組件的功能。Binder就像一張網(wǎng),將整個系統(tǒng)的組件,跨進程和線程的組織在一起。
Binder是整個系統(tǒng)的運行的中樞。Android在進程間傳遞數(shù)據(jù)使用共享內(nèi)存的方式,這樣數(shù)據(jù)只需要復(fù)制一次就能從一個進程到達另一個進城了(前面文章說了,一般IPC都需要兩步,第一步用戶進程復(fù)制到內(nèi)核,第二步再從內(nèi)核復(fù)制到服務(wù)進程。)
PS: 整個Androdi系統(tǒng)架構(gòu)中,雖然大量采用了Binder機制作為IPC(進程間通信)方式,但是也存在部分其他的IPC方式,比如Zygote通信就是采用socket。
5、Binder在Service服務(wù)中的作用
在Android中,有很多Service都是通過Binder來通信的,比如MediaService名下的眾多Service:
- AudioFlinger音頻核心服務(wù)
- AudioPolicyService:音頻策略相關(guān)的重要服務(wù)
- MediaPlayerService:多媒體系統(tǒng)中的重要服務(wù)
- CarmeraService:有關(guān)攝像/照相的重要服務(wù)
那具體是怎么應(yīng)用或者通信機制是什么那?那就讓我們來詳細了解下
(二)、總結(jié)
Android Binder 是在OpenBinder上定制實現(xiàn)的。原先的OpenBinder 框架現(xiàn)在已經(jīng)不再繼續(xù)開發(fā),所以也可以說Android讓原先的OpenBinder得到了重生。Binder是Android上大量使用的IPC(Inter-process communication,進程間通訊)機制。無論是應(yīng)用程序?qū)ο到y(tǒng)服務(wù)的請求,還是應(yīng)用程序自身提供對外服務(wù),都需要使用Binder。
整體架構(gòu)

從圖中可以看出,Binder的實現(xiàn)分為這幾層,按照大的框架理解是
- framewor層
- java 層
- jni 層
- native/ C++層
- linux驅(qū)動層 c語言
讓我們來仔細研究下。
- 其中Linux驅(qū)動層位于Linux內(nèi)核中,它提供了最底層的數(shù)據(jù)傳遞,對象標示,線程管理,通過調(diào)用過程控制等功能。驅(qū)動層其實是Binder機制的核心。
- Framework層以Linux驅(qū)動層為基礎(chǔ),提供了應(yīng)用開發(fā)的基礎(chǔ)設(shè)施。Framework層既包含了C++部分的實現(xiàn),也包含了Java基礎(chǔ)部分的實現(xiàn)。為了能將C++ 的實現(xiàn)復(fù)用到Java端,中間通過JNI進行銜接。
開發(fā)者可以在Framework之上利用Binder提供的機制來進行具體的業(yè)務(wù)邏輯開發(fā)。其實不僅僅是第三方開發(fā)者,Android系統(tǒng)中本身也包含很多系統(tǒng)服務(wù)都是基于Binder框架開發(fā)的。其中Binder框架是典型的C/S架構(gòu)。所以在后面中, 我們把服務(wù)的請求方稱為Client,服務(wù)的實現(xiàn)方稱之Server。Clinet對于Server的請求會經(jīng)由Binder驅(qū)動框架由上至下傳遞到內(nèi)核的Binder驅(qū)動中,請求中包含了Client將要調(diào)用的命令和參數(shù)。請求到了Binder驅(qū)動以后,在確定了服務(wù)的提供方之后,再講從下至上將請求傳遞給具體的服務(wù)。如下圖所示:

如果大家對網(wǎng)絡(luò)協(xié)議有所了解的話,其實會發(fā)現(xiàn)整個數(shù)據(jù)的傳遞過程和網(wǎng)絡(luò)協(xié)議如此的相似。
四、Binder通信機制
Android內(nèi)部采用C/S架構(gòu)。而Binder通信也是采用C/S架構(gòu)。那我們來看下Binder在C/S的中的流程。
(一) Binder在C/S中的流程
如下圖

具體流程如下:
- 1、相應(yīng)的Service需要注冊服務(wù)。Service作為很多Service的擁有者,當它想向Client提供服務(wù)時,得先去Service Manager(以后縮寫成SM)那兒注冊自己的服務(wù)。Server可以向SM注冊一個或者多個服務(wù)。
- 2、Client申請服務(wù)。Client作為Service的使用者,當他想使用服務(wù)時,得向SM申請自己所需要的服務(wù)。Client可以申請一個或者多個服務(wù)。
- 3、當Client申請服務(wù)成功后,Client就可以使用服務(wù)了。
SM一方面管理Server所提供的服務(wù),同時又響應(yīng)Client的請求并為之分配響應(yīng)的服務(wù)。扮演角色相當于月老,兩邊前線。這種通信方式的好處是:一方面,service和Client請求便于管理,另一方面在應(yīng)用程序開發(fā)時,只需要為Client建立到Server的連接即可,這樣只需要花很少的時間成本去實現(xiàn)Server的相應(yīng)功能。那么Binder與這個通信有什么關(guān)系?其實三者的通信方式就是Binder機制(比如Server向SM注冊服務(wù),使用Binder通信;Client申請請求也是Binder通信。)
PS:注意這里的ServiceManager是指Nativie層的ServiceManager(C++),并非是framework層的ServiceManager(Java)。ServiceManager是整個Binder通信機制的大管家,是Android進程間通信機制的守護進程。
(二)Binder通信整體框架
這里先提前和大家說下,后面我們會不斷的提及兩個概念,一個是Server,還有一個是Service,我這里先強調(diào)下,Server是Server,Service是Service,大家不要混淆,一個Server下面可能有很多Service,但是一個Servcie也只能隸屬于一個Server。下面我們將從三個角度來看Binder框架,這樣
1、從內(nèi)核和用戶空間的角度來看
Binder通信模型如下:

我們可以發(fā)現(xiàn):
- 1、Client和Server是存在于用戶空間
- 2、Client和Server通信實現(xiàn)是由Binder驅(qū)動在內(nèi)核的實現(xiàn)
- 3、SM作為守護進程,處理客戶端請求,管理所有服務(wù)
如果大家不好理解上面的意思,我們可以把SM理解成為DNS服務(wù)器,那么Binder Driver就相當于路由的功能。這里就涉及到Client和Server是如何通信的問題。
2、從Android的層級的角度
如下圖:(注意圖片的右邊)

圖中Client/Server/ServiceManager之間的相互通信都是基于Binder機制。圖中Clinet/Server/ServiceManager之間交互都是虛線表示,是由于他們彼此之間不直接交互,都是通過Binder驅(qū)動進行交互,從而實現(xiàn)IPC通信方式。其中Binder驅(qū)動位于內(nèi)核空間,Client/Server/ServiceManager可以看做是Android平臺的基礎(chǔ)架構(gòu)。而Client和Server是Android的應(yīng)用層,開發(fā)人員只需要自定義實現(xiàn)client、Server端,借助Android的基本平臺架構(gòu)就可以直接進行IPC通信。
3、從Binder的架構(gòu)角度來看
如下圖:

同樣
Binder IPC 屬于 C/S 結(jié)構(gòu),Client 部分是用戶代碼,用戶代碼最終會調(diào)用 Binder Driver 的 transact 接口,Binder Driver 會調(diào)用 Server,這里的 Server 與 service 不同,可以理解為 Service 中 onBind 返回的 Binder 對象,請注意區(qū)分下。
Client端:用戶需要實現(xiàn)的代碼,如 AIDL 自動生成的接口類
Binder Driver:在內(nèi)核層實現(xiàn)的 Driver
Server端:這個 Server 就是 Service 中 onBind 返回的 IBinder 對象
需要注意的是,上面綠色的色塊部分都是屬于用戶需要實現(xiàn)的部分,而藍色部分是系統(tǒng)去實現(xiàn)了。也就是說 Binder Driver 這塊并不需要知道,Server 中會開啟一個線程池去處理客戶端調(diào)用。為什么要用線程池而不是一個單線程隊列呢?試想一下,如果用單線程隊列,則會有任務(wù)積壓,多個客戶端同時調(diào)用一個服務(wù)的時候就會有來不及響應(yīng)的情況發(fā)生,這是絕對不允許的。
對于調(diào)用 Binder Driver 中的 transact 接口,客戶端可以手動調(diào)用,也可以通過 AIDL 的方式生成的代理類來調(diào)用,服務(wù)端可以繼承 Binder 對象,也可以繼承 AIDL 生成的接口類的 Stub 對象。這些細節(jié)下面繼續(xù)接著說,這里暫時不展開。
切記,這里 Server 的實現(xiàn)是線程池的方式,而不是單線程隊列的方式,區(qū)別在于,單線程隊列的話,Server 的代碼是線程安全的,線程池的話,Server 的代碼則不是線程安全的,需要開發(fā)者自己做好多線程同步。
(三)、Binder流程
1、Server向SM注冊服務(wù)

- 1、首先 XXServer(XXX代表某個)在自己的進程中向Binder驅(qū)動申請創(chuàng)建一個XXXService的Binder實體。
- 2、Binder驅(qū)動為這個XXXService創(chuàng)建位于內(nèi)核中的Binder實體節(jié)點以及Binder的引用,注意,是將名字和新建的引用打包傳遞給SM(實體沒有傳給SM),通知SM注冊一個名叫XXX的Service。
- 3、SM收到數(shù)據(jù)包后,從中取出XXXService名字和引用,填入一張查找表中
- 4、此時,如果有Client向SM發(fā)送申請服務(wù)XXXService的請求,那么SM就可以查找表中該Service的Binder引用,并把BInder引用(XXXBpBinder返回給Client)
在進一步了解Binder通信機制之前,我們先弄清楚幾個概念。
- 引用和實體。這里,對于一個用于通信的實體(可以理解為真實空間的Object),可以額有多個該實體的引用(沒有真實空間,可以理解成實體的一個鏈接,操作引用就可以操作對應(yīng)鏈接上的實體)。如果一個進程持有某個實體,其他進程也想操作該實體,最高效的做法是去獲取該實體的引用,再去操作這個引用。
- 有些資源也把實體成本本地對象,應(yīng)用稱為遠程對象。所以也可以這么理解:應(yīng)用是從本地進程發(fā)送給其他進程操作實體之用,所以有本地和遠程對象之名。
為了大家在后面更好的理解,這里補充幾個概念
- Binder實體對 :Binder實體對象就是Binder實體對象就是Binder服務(wù)的提供者。一個提供Binder服務(wù)的類必須繼承BBinder類,因此,有時為了強調(diào)對象類型,也用"BBinder對象"來代替"Binder實體對象"。
- Binder引用對象 :Binder引用對象是Binder實體對象在客戶進程的代表,每個引用對象的類型都是BpBiner類,同樣可以用名稱"BpBinder對象"來代替"Binder引用對象"。
- Binder代理對象 :代理對象也成為接口對象,它主要是為了客戶端的上層應(yīng)用提供接口服務(wù),從IInterface類派生。它實現(xiàn)了Binder服務(wù)的函數(shù)接口,當然只是一個轉(zhuǎn)調(diào)的空殼。通過代理對象,應(yīng)用能像使用本地對象一樣使用遠端實體對象提供服務(wù)。
- IBiner對象 :BBinder和BpBinder類是從IBinder類中繼承來。在很多場合,不需要刻意地去區(qū)分實體對象和引用對象,這時候也可以統(tǒng)一使用"IBinder對象"來統(tǒng)一稱呼他們。
- Binder代理對象 主要是和應(yīng)用程序打交道,將Binder代理對象和Binder引用對應(yīng)(BpBinder對象)分開的好處是代理對象可以有很多實例,但是它們包含的是同一個引用對象,這樣方便了應(yīng)用層的使用。如下圖所示:

這樣應(yīng)用層可以直接拋開接口對象直接使用Binder的引用對象,但是這樣開發(fā)的程序兼容性不好。也正是客戶端將引用對象和代理對象分離,Android才能用一套架構(gòu)來同時為Java和native層提供Binder服務(wù)。隔離后,Binder底層不需要關(guān)系上層的實現(xiàn)細節(jié),只需要和Binder實體對象和引用對象進行交互。
PS:BpBinder(Binder引用對象,在客戶端)和BBinder(Binder實體,在服務(wù)端)都是Android中Binder通信相關(guān)的代表,它們都是從IBiner類中派生而來(BpBinder和BBinder在Native層,不在framework層),關(guān)系圖如下:

client端:BpBinder通過調(diào)用transact()來發(fā)送事物請求
server端:BBinder通過onTransact()會接受到相應(yīng)的事物
這時候再來看下這個圖,然后大家思考一下,就會明白很多事情。

2、如何獲得一個SM的遠程接口

如果你足夠細心,你會發(fā)現(xiàn)這里有一個問題:
SM和Server都是進程,Server向SM注冊Binder需要進程間通信,當前實現(xiàn)的是進程間通信,卻又用到進程間通信。有點暈是不,就好像先有雞還是先有蛋這個問題。
其實Binder是這么解決這個問題的:
- 針對Binder的通信機制,Server端擁有的是Binder的實體(BBinder);Client擁有的是Binder的引用(BpBinder)。
- 如果把SM看做Server端,讓它在Binder驅(qū)動一起運行起來時就有自己的實體(BBinder)(代碼中設(shè)置ServiceManager的Binder其handle的值恒為0)。這個Binder實體沒有名字也不需要注冊,所有的Client都認為handle值為0的binder引用(BpBinder)是用來與SM通信的。那么這個問題就解決了。
- 但是問題又來了,Client和Server中handle的值為0(值為0的引用是專門與SM通信用的),還不行,還需要讓SM的handle值為0的實體(BBinder)為0才算大功告成。怎么實現(xiàn)的? 當一個進程調(diào)用Binder驅(qū)動時,使用** "BINDER_SET_CONTEXXT_MGR" ** 命名(在binder_ioctl中)將自己注冊成SM時,Binder驅(qū)動會自動為她創(chuàng)建Binder實體。這個Binder的引用對所有Client都為0。
3、Client從SM中獲得Service的遠程接口
Server向SM注冊了Binder實體及其名字后,Client就可以Service的名字在SM在查找表中獲得了該Binder的引用(BpBinder)了。Client也利用了保留的handle值為0的引用向SM請求訪問某個Service:當申請訪問XXXService的引用。SM就會從請求數(shù)據(jù)包中獲得XXXService的名字,在查找表中找到名字對應(yīng)的條目,取出Binder的引用打包回復(fù)給Client。然后,Client就可以利用XXXService的引用使用XXXService的服務(wù)了。如果有更多的Client請求該Service,系統(tǒng)中就會有更多的Client獲得這個引用。
如下圖

4、建立C/S連接后
首先要明白一個事情:
Client要擁有自己的自己的Binder實體,以及Server的Binder的應(yīng)用;Server有用自己的Binder的實體,以及Client的Binder引用。
我們也可以按照網(wǎng)絡(luò)請求的方式來分析:
- 從Client向Server發(fā)送數(shù)據(jù):Client為發(fā)送方,擁有Binder實體;Server為接收方,擁有Binder引用。
- 從Server向Client發(fā)送數(shù)據(jù):Server為發(fā)送方,擁有Binder實體:Client為接收方,擁有Binder引用。
其實,在我們建立C/S連接后,無需考慮誰是Client,誰是Server。只要理清誰是發(fā)送方,誰是接收方,就能知道Binder的實體和應(yīng)用在那邊。
那我們看下建立C/S連接后的,具體流程,如下圖:

那我們說下具體的流程:
- 第一步,發(fā)送方通過Binder實體請求發(fā)送操作
- 第二步,Binder驅(qū)動會處理這個操作請求,把發(fā)送方的數(shù)據(jù)放入寫緩存(binder_write_read.write_buffer)(對于接受方來說為讀緩存區(qū)),并把read_size(接收方讀數(shù)據(jù))置為數(shù)據(jù)大小。
- 第三步,接收方之前一直在阻塞狀態(tài)中,當寫緩存又數(shù)據(jù),則會讀取數(shù)據(jù),執(zhí)行命令操作
- 第四步,接收方執(zhí)行完后,會把返回結(jié)果同樣采用binder_transaction_data結(jié)構(gòu)體封裝,寫入緩沖區(qū)(對于發(fā)送方,為讀緩沖區(qū))
(四) 匿名Binder
在Android中Binder還可以建立點對點的私有通道,匿名Binder就是這種方式。在Binder通信中,并不是所有通信的Binder實體都需要注冊給SM的,Server可以通過已建立的實體Binder連接將創(chuàng)建的Binder實體傳給Client。而這個Binder沒有向SM注冊名字。這樣Server和Client通信就有很高的隱私性和安全性。
如下圖:

五、 Binder的層次
從代碼上看,Binder設(shè)計的類可以分成4個層級,如下圖所示

- 最上層的是位于Framewok中的各種Binder服務(wù)類和它們的接口類。這一層的類非常多,比如常見的ActivityManagerService(縮寫叫AMS)、WindowManagerService(縮寫叫WMS)、PackageManagerService(縮寫是PMS)等,它們?yōu)閼?yīng)用程序提供了各種各樣的服務(wù)。
- 中間則分為為兩層,上面是用于服務(wù)類和接口開發(fā)的基礎(chǔ),比如IBinder、BBinder、BpBinder等。下層是和驅(qū)動交互的IPCThreadState和ProcessState類。
- 這里刻意把中間的libbinder中的類劃分為兩個層次的原因,是在這4層中,第一層的和第二層聯(lián)系很緊密,第二層中的 各種Binder類用來支撐服務(wù)類和代理類的開發(fā)。但是第三層的IPCThread和第四層之間耦合得很厲害,單獨理解IPCThread或者是驅(qū)動都是一件很難的事,必須把它們結(jié)合起來理解,這一點正是Binder架構(gòu)被人詬病的地方,驅(qū)動和應(yīng)用層之間過于耦合,違反了Linux驅(qū)動設(shè)計的原則,因此,主流的Linux并不愿意接納Binder。
下面我們就來詳細的看來Binder
六、Binder協(xié)議
Biner協(xié)議格式基本是"命令+數(shù)據(jù)",使用ioctl(fd,cmd,arg)函數(shù)實現(xiàn)交互。命令由參數(shù)cmd承載,數(shù)據(jù)由參數(shù)arg,隨著cmd不同而不動。下表列了所有命令及其對應(yīng)的數(shù)據(jù):
| 命令 | 含義 | 參數(shù)(arg) |
|---|---|---|
| BINDER_WRITE_READ | 該命令向Binder寫入或讀取數(shù)據(jù)。參數(shù)分為兩段:寫部分和讀部分。如果write_size不為0,就將write_buffer里的數(shù)據(jù)寫入Binder;如果read_ size不為0再從Binder中讀取數(shù)據(jù)存入read_buffer中。write_consumered和read_consumered表示操作完成時Binder驅(qū)動實際寫入或者讀出數(shù)據(jù)的個數(shù) | struct binder_write_read{ singed long write_size;singed long write_consumed; unsigned long write_buffer; signed long read_size; signed long read_consumed; unsigned long read_buffer; } ; |
| BINDER_SET_MAX_THREADS | 該命令告知Binder驅(qū)動接收方(通常是Server端)線程池中最大的線程數(shù)。由于Client是并發(fā)向Server端發(fā)送請求的,Server端必須開辟線程池為這些并發(fā)請求提供服務(wù) 。告知驅(qū)動線程池的最大值是為了讓驅(qū)動發(fā)現(xiàn)線程數(shù)達到該線程池的最大值是為了讓驅(qū)動發(fā)現(xiàn)線程數(shù)達到該值時,不要再命令接收端啟動先的線程。 | int max_threads; |
| BINDER_SET_CONTEXT_MGR | 當前進程注冊為SM。系統(tǒng)中只能存在一個SM,只要當前的SM沒有調(diào)用close()關(guān)閉,Binder驅(qū)動就不能有別的進程變成SM | 無 |
| BINDER_TREAD_EXIT | 通知Binder驅(qū)動當前線程退出了。Binder會為所有參與的通信線程(包括Server線程池中的線程和Client發(fā)出的請求的線程) 建立相應(yīng)的數(shù)據(jù)結(jié)構(gòu)。這些線程在退出時必須通知驅(qū)動釋放相應(yīng)的數(shù)據(jù)結(jié)構(gòu) | 無 |
| BINDER_VERSION | 獲取Binder驅(qū)動的版本號 | 無 |
這其中最常用的命令是 BINDER_WRITE_READ。該命令的參數(shù)包括兩個部分:
- 1、是向Binder寫入數(shù)據(jù)
- 2、是向Binder讀出數(shù)據(jù)
驅(qū)動程序先處理寫部分再處理讀部分。這樣安排的好處是應(yīng)用程序可以很靈活的地處理命令的同步或者異步。例如若要發(fā)送異步命令可以只填入寫部分而將read_size設(shè)置為0,若要只從Binder獲得的數(shù)據(jù)可以將寫部分置空,即write_size置0。如果想要發(fā)送請求并同步等待返回數(shù)據(jù)可以將兩部分都置上。
(一)、BINDER_WRITE_READ 之寫操作
Binder寫操作的數(shù)據(jù)時格式同樣也是(命令+數(shù)據(jù))。這時候命令和數(shù)據(jù)都存放在binder_write_read結(jié)構(gòu)中的write_buffer域指向的內(nèi)存空間里,多條命令可以連續(xù)存放。數(shù)據(jù)緊接著存放在命令后面,格式根據(jù)命令不同而不同。下表列舉了Binder寫操作支持的命令:
我提供兩套,一套是圖片,方便手機用戶,一部分是文字,方便PC用戶

上面圖片,下面是文字
| 命令 | 含義 | 參數(shù)(arg) | |
|---|---|---|---|
| BC_TRANSACTION BC_REPLY | BC_TRANSACTION用于Client向Server發(fā)送請求數(shù)據(jù);BC_REPLY用于Server向Client發(fā)送回復(fù)(應(yīng)答)數(shù)據(jù)。其后面緊接著一個binder_transaction_data結(jié)構(gòu)體表明要寫入的數(shù)據(jù)。 | struct binder_transaction_data | |
| BC_ACQUIRE_RESULT | 暫未實現(xiàn) | ||
| BC_FREE_BUFFER | 釋放一塊映射內(nèi)存。Binder接受方通過mmap()映射一塊較大的內(nèi)存空間,Binder驅(qū)動基于這片內(nèi)存采用最佳匹配算法實現(xiàn)接受數(shù)據(jù)緩存的動態(tài)分配和釋放,滿足并發(fā)請求對接受緩存區(qū)的需求。應(yīng)用程序處理完這篇數(shù)據(jù)后必須盡快使用費改命令釋放緩存區(qū),否則會因為緩存區(qū)耗盡而無法接受新數(shù)據(jù) | 指向需要釋放的緩存區(qū)的指針;該指針位于收到的Binder數(shù)據(jù)包中 | |
| BC_INCREFS BC_ACQUIRE BC_RELEASE BC_DECREFS | 這組命令增加或減少Binder的引用計數(shù),用以實現(xiàn)強指針或弱指針的功能 | 32位Binder引用號 | |
| BC_REGISTER_LOOPER BC_ENTER_LOOPER BC_EXIT_LOOPER | 這組命令同BINDER_SET_MAX_THREADS 一并實現(xiàn)Binder驅(qū)動對接收方線程池的管理。BC_REGISTER_LOOPER通知驅(qū)動線程池中的一個線程已經(jīng)創(chuàng)建了;BC_ENTER_LOOPER通知該驅(qū)動線程已經(jīng)進入主循環(huán),可以接受數(shù)據(jù);BC_EXIT_LOOPER通知驅(qū)動該線程退出主循環(huán),不在接受數(shù)據(jù)。 | ----- | |
| BC_REQUEST_DEATH_NOTIFICATION | 獲得Binder引用的進程通過該命令要求驅(qū)動在Binder實體銷毀得到通知。雖說強指針可以確保只要有引用就不會銷毀實體,但這畢竟是個跨進程的引用,誰也無法保證實體由于所在的Server關(guān)閉Binder驅(qū)動或異常退出而消失,引用者能做的就是要求Server在此刻給出通知 | uint32 *ptr;需要得到死亡的通知Binder引用 | void **cookie:與死亡通知相關(guān)的信息,驅(qū)動會在發(fā)出死亡通知時返回給發(fā)出請求的進程。 |
| BC_DEAD_BINDER | 收到實體死亡通知書的進程在刪除引用后用本命令告知驅(qū)動 | void * cookie |
在這些命令中,最常用的h是BC_TRANSACTION/BC_REPLY命令對,Binder請求和應(yīng)答數(shù)據(jù)就是通過這對命令發(fā)送給接受方。這對命令所承載的數(shù)據(jù)包由結(jié)構(gòu)體struct binder_transaction_data定義。Binder交互有同步和異步之分。利用binder_transcation_data中的flag區(qū)域劃分。如果flag區(qū)域的TF_ONE_WAY位為1,則為異步交互,即client發(fā)送完請求交互即結(jié)束,Server端不再返回BC_REPLY數(shù)據(jù)包;否則Server會返回BC_REPLY數(shù)據(jù)包,Client端必須等待接受完數(shù)據(jù)包后才能完成一次交互。
(二)、BINDER_WRITE_READ:從Binder讀出數(shù)據(jù)
在Binder里讀出數(shù)據(jù)格式和向Binder中寫入數(shù)據(jù)格式一樣,采用(消息ID+數(shù)據(jù))形式,并且多條消息可以連續(xù)存放。下面列舉從Binder讀出命令及相應(yīng)的參數(shù)。
為了照顧手機端的朋友,先發(fā)圖片

Binder讀操作消息ID
| 消息 | 含義 | 參數(shù)(arg) |
|---|---|---|
| BR_ERROR | 發(fā)生內(nèi)部錯誤(如內(nèi)存分配失敗) | ---- |
| BR_OK BR_NOOP | 操作完成 | ---- |
| BR_SPAWN_LOOPER | 該消息用于接受方線程池管理。當驅(qū)動發(fā)現(xiàn)接收方所有線程都處于忙碌狀態(tài)且線程池中的線程總數(shù)沒有超過BINDER_SET_MAX_THREADS設(shè)置的最大線程時,向接收方發(fā)送該命令要求創(chuàng)建更多的線程以備接受數(shù)據(jù)。 | ----- |
| BR_TRANSCATION BR_REPLY | 這兩條消息分別對應(yīng)發(fā)送方的 BC_TRANSACTION 和BC_REPLY,表示當前接受的數(shù)據(jù)是請求還是回復(fù) | binder_transaction_data |
| BR_ACQUIRE_RESULT BR_ATTEMPT_ACQUIRE BR_FINISHED | 尚未實現(xiàn) | ----- |
| BR_DEAD_REPLY | 交互過程中如果發(fā)現(xiàn)對方進程或線程已經(jīng)死亡則返回該消息 | ----- |
| BR_TRANSACTION_COMPLETE | 發(fā)送方通過BC_TRRANSACTION或BC_REPLY發(fā)送完一個數(shù)據(jù)包后,都能收到該消息作為成功發(fā)送的反饋。這和BR_REPLY不一樣,是驅(qū)動告知發(fā)送方已經(jīng)發(fā)送成功,而不是Server端返回數(shù)據(jù)。所以不管同步還是異步交互接收方都能獲得本消息。 | ----- |
| BR_INCREFS BR_ACQUIRE BR_RFLEASE BR_DECREFS | 這組消息用于管理強/弱指針的引用計數(shù)。只有提供Binder實體的進程才能收到這組消息 | void *ptr : Binder實體在用戶空間中的指針 void **cookie:與該實體相關(guān)的附加數(shù)據(jù) |
| BR_DEAD_BINDER BR_CLEAR_DEATH_NOTIFICATION_DONE | 向獲得Binder引用的進程發(fā)送Binder實體死亡通知書:收到死亡通知書的進程接下來會返回 BC_DEAD_BINDER_DONE 確認 | void *cookie 在使用BC_REQUEST_DEATH_NOTIFICATION注冊死亡通知時的附加參數(shù) |
| BR_FAILED_REPLY | 如果發(fā)送非法引用號則返回該消息 | ----- |
和寫數(shù)據(jù)一樣,其中最重要的消息是BR_TRANSACTION或BR_REPLY,表明收到一個格式為binder_transaction_data的請求數(shù)據(jù)包(BR_TRANSACTION或返回數(shù)據(jù)包(BR_REPLY))
(三)、struct binder_transaction_data :收發(fā)數(shù)據(jù)包結(jié)構(gòu)
該結(jié)構(gòu)是Binder接收/發(fā)送數(shù)據(jù)包的標準格式,每個成員定義如下:
下圖是Binder

| 成員 | 含義 |
|---|---|
| union{ size_t handle; void *ptr;} target; | 對于發(fā)送數(shù)據(jù)包的一方,該成員指明發(fā)送目的地。由于目的地是遠端,所以在這里填入的是對Binder實體的引用,存放在target.handle中。如前述,Binder的引用在代碼中也叫句柄(handle)。 當數(shù)據(jù)包到達接收方時,驅(qū)動已將該成員修改成Binder實體,即指向 Binder對象內(nèi)存的指針,使用target.ptr來獲取。該指針是接受方在將Binder實體傳輸給其他進程時提交給驅(qū)動的,驅(qū)動程序能夠自動將發(fā)送方填入的引用轉(zhuǎn)換成接收方的Binder對象的指針,故接收方可以直接將其當對象指針來使用(通常是將其reinpterpret_cast相應(yīng)類) |
| void *cookie; | 發(fā)送方忽略該成員;接收方收到數(shù)據(jù)包時,該成員存放的是創(chuàng)建Binder實體時由該接收方自定義的任意數(shù)值,做為與Binder指針相關(guān)的額外信息存放在驅(qū)動中。驅(qū)動基本上不關(guān)心該成員 |
| unsigned int code ; | 該成員存放收發(fā)雙方約定的命令碼,驅(qū)動完全不關(guān)心該成員的內(nèi)容。通常是Server端的定義的公共接口函數(shù)的編號 |
| unsigned int code; | 與交互相關(guān)的標志位,其中最重要的是TF_ONE_WAY位。如果該位置上表明這次交互是異步的,Server端不會返回任何數(shù)據(jù)。驅(qū)動利用該位決定是否構(gòu)建與返回有關(guān)的數(shù)據(jù)結(jié)構(gòu)。另外一位TF_ACCEPT_FDS是處于安全考慮,如果發(fā)起請求的一方不希望在收到回復(fù)中接收文件的Binder可以將位置上。因為收到一個文件形式的Binder會自動為接收方打開一個文件,使用該位可以防止打開文件過多 |
| pid_t send_pid uid_t sender_euid | 該成員存放發(fā)送方的進程ID和用戶ID,由驅(qū)動負責(zé)填入,接收方可以讀取該成員獲取發(fā)送方的身份。 |
| size_t data_size | 驅(qū)動一般情況下不關(guān)心data.buffer里存放了什么數(shù)據(jù)。但如果有Binder在其中傳輸則需要將其對應(yīng)data.buffer的偏移位置指出來讓驅(qū)動知道。有可能存在多個Binder同時在數(shù)據(jù)中傳遞,所以須用數(shù)組表示所有偏移位置。本成員表示該數(shù)組的大小。 |
| union{ struct{ const void *buffer; const void * offset; } ptr; uint8_t buf[8];} data; | data.buffer存放要發(fā)送或接收到的數(shù)據(jù);data.offsets指向Binder偏移位置數(shù)組,該數(shù)組可以位于data.buffer中,也可以在另外的內(nèi)存空間中,并無限制。buf[8]是為了無論保證32位還是64位平臺,成員data的大小都是8字節(jié)。 |
PS:這里有必要強調(diào)一下offsets_size和data.offsets兩個成員,這是Binder通信有別于其他IPC的地方。就像前面說說的,Binder采用面向?qū)ο蟮脑O(shè)計思想,一個Binder實體可以發(fā)送給其他進程從而建立許多跨進程的引用;另外這些引用也可以在進程之間傳遞,就像java將一個引用賦值給另外一個引用一樣。為Binder在不同進程中創(chuàng)建引用必須有驅(qū)動參與,由驅(qū)動在內(nèi)核創(chuàng)建并注冊相關(guān)的數(shù)據(jù)結(jié)構(gòu)后接收方才能使用該引用。而且這些引用可以是強類型的,需要驅(qū)動為其維護引用計數(shù)。然后這些跨進程傳遞的Binder混雜在應(yīng)用程序發(fā)送的數(shù)據(jù)包里,數(shù)據(jù)格式由用戶定義,如果不把他們一一標記出來告知驅(qū)動,驅(qū)動將無法從數(shù)據(jù)中將他們提取出來。于是就是使用數(shù)組data.offsets存放用戶數(shù)據(jù)中每個Binder相對于data.buffer的偏移量,用offersets_size表示這個數(shù)組的大小。驅(qū)動在發(fā)送數(shù)據(jù)包時會根據(jù)data.offsets和offset_size將散落于data.buffer中的Binder找出來并一一為它們創(chuàng)建相關(guān)的數(shù)據(jù)結(jié)構(gòu)。
七、Binder的整體架構(gòu)
