通過最近對Binder的源碼閱讀,以及拜讀了各路大神的文章后封恰,再次對自己的理解做一個小小的記錄。
Binder 是什么
Binder 是 Android 系統(tǒng)中的一中進(jìn)程間通信的機(jī)制惕味,前身是 OpenBinder扇调,Binder 是 Android 系統(tǒng)最重要的組件之一,也是整個系統(tǒng)的基石在讶。
同時(shí)煞抬,Binder 也是一個系統(tǒng)設(shè)備 /dev/binder 對應(yīng)的是 Binder 驅(qū)動,是 Binder 的核心构哺。
從實(shí)現(xiàn)機(jī)制上來說革答,Binder是一個token,用來標(biāo)識一個服務(wù)曙强,這個token可以跨進(jìn)程地傳遞残拐,拿到binder對象,就可以發(fā)起遠(yuǎn)程訪問碟嘴。
Linux 常見的進(jìn)程間通信方式及特點(diǎn)
共享內(nèi)存
無需拷貝
邏輯復(fù)雜溪食,需要應(yīng)用程序處理進(jìn)程間數(shù)據(jù)同步
信號量
適合事件通知,不適合傳遞大量數(shù)據(jù)
Socket
速度慢娜扇,兩次拷貝错沃,適合低速通信,或跨設(shè)備通信
文件共享
速度慢雀瓢,同步狀態(tài)難以維護(hù)
為什么采用 Binder
- 效率高枢析,僅需一次拷貝
- 封裝完善,由系統(tǒng)統(tǒng)一管理進(jìn)程間的數(shù)據(jù)同步刃麸、線程池管理等等醒叁,使用方便
- 語法簡潔,可以以同步的方式完成ipc通信嫌蚤,使程序邏輯更簡單
- 支持 Java 層辐益,Native 層
- 由系統(tǒng)統(tǒng)一處理uid,方便進(jìn)行權(quán)限控制脱吱,安全性高
- AIDL 工具支持直接生成Binder代碼智政,方便使用
Binder 主要概念
IBinder
代表一個可以遠(yuǎn)程訪問的對象,所謂遠(yuǎn)程訪問箱蝠,就是跨進(jìn)程訪問续捂。
IBinder主要有幾個方法:
- getInterfaceDescriptor(): 獲取這個對象的名稱
- pingBinder(): 檢查這個對象是否還存在
- isBinderAlive():檢查提供這個Binder對象的進(jìn)程是否還存活
- queryLocalInterface(@NonNull String descriptor): 查詢本地接口垦垂,對于同進(jìn)程使用 Binder,這個方法會直接返回 Binder 的實(shí)現(xiàn)類牙瓢,對于跨進(jìn)程的場景劫拗,這個方法返回 null
- transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags): Binde r的核心方法,用來向遠(yuǎn)程服務(wù)發(fā)送數(shù)據(jù)
- linkToDeath: 死亡監(jiān)聽
IInterface
Binder服務(wù)的基礎(chǔ)接口矾克,只有一個方法:asBinder页慷,用來獲取對應(yīng)的Binder對象。IInterface 主要是用來作為服務(wù)端和客戶端的一個協(xié)議規(guī)范胁附,即服務(wù)端實(shí)現(xiàn)相應(yīng)的方法酒繁,客戶端來調(diào)用,這個規(guī)范就是一個IInterface控妻。
Parcel
Parcel 是一個支持?jǐn)?shù)據(jù)序列化的組件州袒,我們知道,在進(jìn)程間傳遞消息弓候,由于進(jìn)程間對象郎哭、類型都不共用,因此必須序列化成通用的數(shù)據(jù)菇存,才能進(jìn)行傳遞夸研,Parcel就是用來做這個事情的,我們常用的Parcelable撰筷,就是基于Parcel實(shí)現(xiàn)的陈惰。在 binder transact 過程中使用。
Binder
這里我們說的是Java層的Binder類毕籽,這個類是IBinder的一個基本實(shí)現(xiàn),實(shí)現(xiàn)了 Binder 的本地調(diào)用(同進(jìn)程)井辆,即 transact 方法直接調(diào)用 onTransact 方法关筒,也是在這里引入了 onTransact方法,我們在使用 AIDL 的時(shí)候杯缺,Stub 類就是 Binder 的子類蒸播。
對于 Native 層的 Binder,作用也是一樣的萍肆,只不過是 Native 的實(shí)現(xiàn)袍榆,用來給 Native 代碼使用。
BinderInternal
這里提供一些 Binder 的內(nèi)部 API(不給app使用的)塘揣,常見的比如:
joinThreadPool: 將調(diào)用線程加入Binder線程池
getContextObject: 獲取系統(tǒng)的“全局上下文”(Global Context)包雀,其實(shí)就是 0 號 Binder,也就是 ServiceManager亲铡,這個方法在 ServiceManager 的 getIServiceManager 方法里面有使用到才写,就是用來獲取 ServiceManager 的 Binder 對象葡兑,這個后續(xù) ServiceManager 中會介紹。
setMaxThreads:
BinderProxy
很重要的一個類赞草,是 Java 層對 Native Binder 的一個代理讹堤,由于 Binder 的主要實(shí)現(xiàn)在 Native 層,如果 Java 層要使用厨疙,就要通過這個 BinderProxy洲守,這個類的對象是由 Native 的 javaObjectforIBinder 方法創(chuàng)建的,在 Java 層沾凄,不會創(chuàng)建這個對象梗醇。
BinderProxy 主要是定義了一堆的 native 方法,用來實(shí)現(xiàn) Java 層和 Native 層的調(diào)用搭独,其中最主要的是 transactNative 方法婴削,這個方法就是 Java 層 transact 的根本所在,也就是走到了 Native 層的 transact 邏輯牙肝。
對應(yīng)的 Native 實(shí)現(xiàn)唉俗,在 android_util_Binder.cpp 這個文件中。
BBinder
BBinder 就是 Binder 的 Native 實(shí)現(xiàn)配椭,也就是說虫溜,這個是在提供服務(wù)的地方創(chuàng)建的,用來處理 Binde r的邏輯股缸,這個在上面 Binder 的地方有提到過了衡楞。
BpBinder
BpBinder 是 Native 層最重要的類之一,我們知道敦姻,Binder 是一中 C/S 架構(gòu)瘾境,而 BpBinder 就是 Client 端真正做事的那個人,不管是 Java 的 BinderProxy镰惦,還是直接 Native 使用 Binder迷守,最終發(fā)送和接收數(shù)據(jù)的就是 BpBinder,transact 最終的實(shí)現(xiàn)就是在這里旺入,BpBinder 調(diào)用 IPCThreadState兑凿,實(shí)現(xiàn) IPC 請求,和結(jié)果的監(jiān)聽茵瘾±窕可以看看 transact 方法的代碼:
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// Once a binder has died, it will never come back to life.
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
ProcessState
這個是 Binder 環(huán)境的初始化類,看名字是進(jìn)程相關(guān)的拗秘,這個類是單例的圣絮,在創(chuàng)建的時(shí)候,會打開 Binder 驅(qū)動聘殖,并完成和 Binder 驅(qū)動的關(guān)系的建立晨雳,也就是通過 mmap 來做一個內(nèi)存映射行瑞,這樣 Binder 驅(qū)動就可以對這個進(jìn)程發(fā)送 transact 數(shù)據(jù)。
IPCThreadState
這個是 Binder 線程相關(guān)的餐禁,最終和 Binder 驅(qū)動交換數(shù)據(jù)的工作是由它來完成的血久。
Binder Driver
Binder 驅(qū)動,Binder 通信機(jī)制的核心帮非,主要的工作就是
- 維護(hù)進(jìn)程間的 binder 對象氧吐,保存引用
- 在進(jìn)程間傳遞消息
ServiceManager
ServiceManager 是一個管理器,可以理解為 binder 服務(wù)的一個管理處末盔,主要是兩個作用:
- 注冊服務(wù)
- 獲取服務(wù)
對于比如 ActivityManagerService筑舅、WindowManagerService 等系統(tǒng)服務(wù),都是有在 ServiceManager 中注冊過的陨舱,因此他們叫做“實(shí)名Binder”翠拣,而未注冊的就叫“匿名Binder”
有一種說法很形象,Binder 作為一個 C/S 架構(gòu)的通信機(jī)制游盲,binder 就是 每個服務(wù)的 IP 和端口误墓,而 ServiceManager 就是一個 DNS 服務(wù)器,將 名稱 映射到對應(yīng)的binder益缎,方便客戶端通過名稱訪問服務(wù)谜慌。
Binder 一次拷貝的原理
最根本的就是使用了mmap進(jìn)行了內(nèi)存的映射,上面有提到莺奔,在ProcessState初始化的時(shí)候欣范,會進(jìn)行一個mmap,這是服務(wù)端進(jìn)程(用戶空間)到binder驅(qū)動(內(nèi)核空間)的一個映射令哟,而在binder驅(qū)動內(nèi)部恼琼,也采用mmap對不同進(jìn)程的緩沖區(qū)做了mmap,于是就相當(dāng)于從客戶端的內(nèi)核空間的一塊內(nèi)存屏富,直接映射到了服務(wù)端的用戶空間的一塊內(nèi)存驳癌,因此只需要從客戶端拷貝一次數(shù)據(jù)到內(nèi)核空間,數(shù)據(jù)就會自動到達(dá)服務(wù)端的用戶空間役听,這就是只需要一次拷貝的原因。
具體可參考此文章:https://blog.csdn.net/AndroidStudyDay/article/details/93749470
Binder 調(diào)用過程
這個問題要區(qū)分客戶端和服務(wù)端
客戶端
對于客戶端表窘,調(diào)用過程如下: 業(yè)務(wù)方法 -> BinderProxy.transact -> BpBinder.transact -> IPCThreadState.transact -> talkWithDriver -> binder_ioctl (驅(qū)動部分省略)
服務(wù)端
對于服務(wù)端典予,主要是Binder線程不停地輪詢,調(diào)用IPCThreadState.getAndExcuteCommand -> talkWithDriver -> binder_ioctl -> executeCommand -> BBinder.transact -> BBinder.onTransact
而 BBinder 是怎么調(diào)用到Java層的Binder對象的方法呢乐严,秘密在android_util_Binder的JavaBBinder中:
const char* const kBinderPathName = "android/os/Binder";
static int int_register_android_os_Binder(JNIEnv* env)
{
jclass clazz = FindClassOrDie(env, kBinderPathName);
...
gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, "execTransact", "(IJJI)Z");
...
}
也就是說是通過這邊瘤袖,實(shí)現(xiàn)Native到Java層的調(diào)用的。
所以服務(wù)端的完整流程是: IPCThreadState.getAndExcuteCommand -> talkWithDriver -> binder_ioctl -> executeCommand -> BBinder.transact -> BBinder.onTransact -> JavaBBinder.onTransact -> Binder.execTrasact -> Binder.onTransact
Java Binder 和 Native Binder 聯(lián)系
Java Binder 是對 Native Binder的封裝昂验,方便Java 層使用捂敌,兩者交互的邏輯主要在 android_util_Binder.cpp 中艾扮。
實(shí)名 Binder,匿名 Binder 區(qū)別與聯(lián)系
上面有提到過占婉,在ServiceManager中登記過的泡嘴,就叫實(shí)名Binder,其他的叫匿名Binder逆济,對于匿名Binder酌予,需要有實(shí)名Binder作為媒介,才能建立通信奖慌,否則Binder無法在進(jìn)程間傳遞抛虫。
參考
http://gityuan.com/2016/09/04/binder-start-service/
https://blog.csdn.net/universus/article/details/6211589
https://blog.csdn.net/AndroidStudyDay/article/details/93749470