Android進程間通信機制Binder
注:本文大部分代碼來自安卓11
● 從IPC角度來說,Binder是Android中的一種跨進程通信方式,Binder還可以理解為一種虛擬的物理設備驅動,它的設備驅動是/dev/binder,該通信方式以前在linux中沒有
● 從Android Framework角度來說,Binder是ServiceManager連接各種Manager(ActivityManager、WindowManager)和相應ManagerService的橋梁
● 從Android應用層來說,Binder是客戶端和服務端進行通信的媒介,當你bindService的時候,服務端會返回一個包含了服務端業(yè)務調用的Binder對象,通過這個Binder對象,客戶端就可以獲取服務端提供的服務或者數據,這里的服務包括普通服務和基于AIDL的服務(本地和遠程)
為什么需要進程間通信?
在操作系統(tǒng)中,進程與進程之間的內存、調度優(yōu)先級等是不共享(都運行與用戶空間,彼此獨立)的,進程之間無法直接訪問對方的數據,因而進程間的數據交互需要采用特殊的方式(需要內核空間支持)進行,也就是進程間通信。
為什么不用已有的進程間通信機制?
Linux系統(tǒng)本身有許多IPC手段,為什么Android要重新設計一套Binder機制呢?
為什么選用Binder,我們知道Android也是基于Linux內核開發(fā)的,Linux現有的進程通信手段有以下幾種:
a. 管道:在創(chuàng)建時分配一個page大小的內存,緩存區(qū)大小比較有限;
b. 消息隊列:信息復制兩次,額外的CPU消耗;不合適頻繁或信息量大的通信;
c. 共享內存:無須復制,共享緩沖區(qū)直接付附加到進程虛擬地址空間,速度快;但進程間的同步問題操作系統(tǒng)無法實現,必須各進程利用同步工具解決;
d. 套接字(Socket):作為更通用的接口,傳輸效率低,主要用于不同機器或跨網絡的通信;
e. 信號量:常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作為進程間以及同一進程內不同線程之間的同步手段。
既然有現有的IPC方式,為什么重新設計一套Binder機制呢。主要是出于以上三個方面的考量:
- 從性能上:從數據拷貝次數來看Binder只需要進行一次內存拷貝,而管道、消息隊列、Socket都需要兩次,共享內存不需要拷貝,Binder的性能僅次于共享內存。
- 從穩(wěn)定性:上面說到共享內存的性能優(yōu)于Binder,那為什么不適用共享內存呢,因為共享內存需要處理并發(fā)同步問題,控制負責,容易出現死鎖和資源竟爭,穩(wěn)定性較差。而Binder基于C/S架構,客戶端與服務端彼此獨立,穩(wěn)定性較好。
- 安全性:Binder會為你注冊的服務分配一個UID,用來作為鑒別進程的重要標志,如果你的服務是惡意軟件啟動的服務,那么我們就可以通過UID找到這個服務并進行禁止。傳統(tǒng)IPC是由服務自己往數據包里填入UID/PID去告知內核,這個標記完全是在用戶空間控制的,沒有放在內核空間,因此有被惡意篡改的可能,篡改后就無法通過此uid找到具體的惡意服務了。因此Binder的安全性更高。
而管道、消息隊列、Socket都需要兩次數據拷貝,共享內存不需要拷貝,指的是:
答:用戶空間的兩個進程是無法直接進行數據交互的,它們之間的通信需要內核空間提供支持
從用戶空間(發(fā)送數據方的用戶空間進程)拷貝到內核空間算一次,
再從內核空間拷貝到用戶空間(接受數據方的用戶空間進程)算第二次
共享內存直接在內核空間中,發(fā)送方和接受方直接訪問,不需要拷貝
Binder因為不是Linux內核的一部分,它是通過一種Linux的動態(tài)內核可加載模塊這一機制來實現的。
動態(tài)內核可加載模塊:是一種可以單獨編譯,但是不能獨立運行的模塊,它在運行時會被鏈接到內核作為內核的一部分運行。
Android系統(tǒng)就是通過動態(tài)添加一個內核模塊在內核空間運行,然后以這個內核模塊為橋梁實現的進程間通信。這個內核模塊就是我們平時所說的Binder驅動(Binder Drivers)
基本原理
步驟 過程描述
虛線就是代表Client、Server不是直接與ServiceManager直接交互,而是通過Binder驅動
Linux內存映射:
內存映射可以將用戶空間的一塊內存區(qū)域映射到內核空間,用戶空間對這塊區(qū)域的修改可以直接映射到內核空間,
反之內核空間對這塊內存區(qū)域的修改也可以映射到用戶空間。實際上在操作系統(tǒng)的表示是一個物理頁被內核空間
和用戶空間同時引用
前置知識
Android智能指針
在Android系統(tǒng)的應用程序框架層中,有相當一部分代碼是使用C++語言開發(fā)的。C++指針使用不當,輕則造成內存泄漏,重則造成莫名其妙的邏輯錯誤,甚至系統(tǒng)崩潰。因此,Android系統(tǒng)為我們提供了C++智能指針,通過引用計數來維護對象的生命周期,Binder對象就是如此。
引用計數技術存在造成循環(huán)引用的問題,解決方法是將對象分為強引用和弱引用兩種進行引用計數。在使用強引用計數和弱引用計數的解決方案中,一般將有關聯的對象劃分為“父-子”和“子一父”關系。在“父-子”關系中,“父”對象通過強引用計數來引用“子”對象;而在“子-父”關系中,“子”對象通過弱引用計數來引用“父”對象。這樣就可以解決由于相互引用而造成對象不能釋放的問題了。被弱引用引用的對象釋放不受影響,被強引用引用的對象釋放受約束,不可用隨便回收釋放。
Binder對象引用計數技術
其實在Client進程和Server進程的一次通信過程中,涉及了四種類型的對象,它們分別是位于Binder驅動程序中的Binder實體對象(binder_node)和Binder引用對象(binder_ref),以及位于Binder庫中的Binder本地對象(BBinder)和Binder代理對象(BpBinder),它們的交互過程如圖5-17所示。
它們的交互過程可以劃分為五個步驟,如下所示。
(1)運行在Client進程中的Binder代理對象通過Binder驅動程序向運行在Server進程中的Binder本地對象發(fā)出一個進程間通信請求,Binder驅動程序接著就根據Client進程傳遞過來的Binder代理對象的句柄值來找到對應的Binder引用對象(查找引用對象的紅黑樹的過程)。
(2)Binder驅動程序根據前面找到的Binder引用對象找到對應的Binder實體對象(通過該節(jié)點的node域找到對應的binder_node節(jié)點,因為binder_ref和binder_node都處于binder驅動的地址空間中,所以是可以用指針直接指向的),并且創(chuàng)建一個事務(binder_transaction)來描述該次進程間通信過程。
(3)Binder驅動程序根據前面找到的Binder實體對象(從屬服務進程的)來找到運行在Server進程中的Binder本地對象(通過binder_node節(jié)點的cookie域),并且將Client進程傳遞過來的通信數據發(fā)送給它處理。
(4)Binder本地對象處理完成Client進程的通信請求之后,就將通信結果返回給Binder驅動程序,Binder驅動程序接著就找到前面所創(chuàng)建的一個事務。
(5)Binder驅動程序根據前面找到的事務的相關屬性來找到發(fā)出通信請求的Client進程,并且通知Client進程將通信結果返回給對應的Binder代理對象處理。
從這個過程就可以看出,Binder代理對象依賴于Binder引用對象,而Binder引用對象又依賴于Binder實體對象,最后,Binder實體對象又依賴于Binder本地對象。這樣,Binder進程間通信機制就必須采用一種技術措施來保證,不能銷毀一個還被其他對象依賴著的對象。為了維護這些Binder對象的依賴關系,Binder進程間通信機制采用引用計數技術來維護每一個Binder對象的生命周期。
Binder_ref和binder_node是紅黑樹結構,說明了進程之間通信的很復雜,進程A可能作為Client與B,C通信, 同時也可能作為Server與D,E通信,和不同的進程通信又是一個不同的Binder_ref和Binder_node節(jié)點
架構分析
從不同層次進行分析
從內核空間(Kernel)與用戶空間(Java、C程序庫)角度看
- 框架層:框架是一個中間層,它對接了底層驅動的實現,封裝了復雜的內部邏輯,并提供對外使用的
內部接口,它分為C++和Java兩個部分,為了實現功能的復用,中間用JNI銜接。
Binder Java
Binder JNI
Binder C - 驅動層:驅動層位于Linux內核中,它提供了最底層的數據傳遞、對象標識、線程管理、調度過程控制 等功能。
接下來我們看Binder體系各個類之間的關系。Java層、JNI層、C++層
這個圖比較全面,我將不同的模塊打上了不同的顏色。
● 黃色:IInterface接口,包含Java層的接口和C++層的接口,它們的功能是一樣的(下層封裝給上層用)。
● 綠色:IBinder接口,包含Java層的接口和C++層的接口,它們的功能是一樣的(下層封裝給上層用)。
● 紅色:本地Binder (同一進程時用)
● 灰色:遠程調用的代理Binder (不同進程時用)
● 紫色:上面都是在說接口,這一塊就是可以由我們用戶實現的位置。這里則是指兩個具體的服務(Client與Server),包含Java層的服務ActivityManagerService(使用Binder Java層實現IPC,底層調用Binder Native層)
Binder體系的核心實現都在C++層,最為核心的就是BpBinder和BBinder了。各個類的作用如下所示:
Java層:
frameworks/base/core/java/android/os
frameworks/base/core/java/com/android/internal/os
● Ilnterface:供Java層Binder服務繼承的接口。
● IBinder:Java層的IBinder類,提供transact()方法來調用遠程的服務。
● Binder:實現了IBinder接口,封裝了JNI的實現,Java層Binder服務的基類。
● BinderProxy:實現了IBinder接口,封裝了JNI實現,提供remote()方法調用遠程服務。
● Parcel:Java層的數據包裝器,底層實現在C++層。
當是跨進程時ActivityManagerProxy就是通過ActivityManagerNative的asInterface進行asInterface(BinderProxy)轉化來的,當不是跨進程時,則是直接就能用的
BinderProxy代表從ServiceManager拿到的一個遠程服務代理對象
IActivityManager代表一個aidl接口類
ActivityManagerNative代表生成的中間服務類(舊方式我們手寫的,新方式會根據你寫的aidl文件自動生成的中間類,里面就包含這些了,具體后面Binder通信的Jav接口討論)
C++層:
frameworks/native/include/binder/Binder.h
frameworks/native/include/binder/
frameworks/native/libs/binder/
frameworks/native/cmds/servicemanager/
● RefBase:繼承了RefBase類的子類對象均可以通過強指針和弱指針來維護它們的生命周期(是一個在引用計數依賴中解決循環(huán)依賴的方法,這也說明了BInder本地對象是通過引用計數技術來維護生命周期的。)
————————————
● BpRefBase:RefBase的子類,提供remote方法獲取遠程Binder
BpRefase類繼承了RefBase類,因此它的子類對象,即Binder代理對象也可以通過強指針和弱指針來維護生命周期。BpRefBase類有一個重要的成員變量mRemote,它指向一個BpBinder對象,可以通過成員函數remote來獲取。BpBinder類實現了BpRefBase類的進程間通信接口
————————————
● IInterfatce:Binder服務接口的基類,Binder服務通常需要同時提供本地接口Bnlnterface和遠程接口BpInterface,這兩個接口都繼承于Ilnterface,擁有相同的方法,本地接口實現服務,遠程接囗提供給Client調用。
○ onAsBinder():由子類實現,在本地對象的實現類中返回本地對象。在遠程對象的實現類中返回
遠程對象。onAsBinder()會被靜態(tài)方法asBinder()調用
● BpInterface:遠程接口的基類,遠程接口是供Client調用的接口集。
● BnInterface:本地接口的基類,本地接口是需要服務中真正實現的接口集。
● IBinder:Binder對象的基類,這個接口定義了與遠程對象交互的協議。
————————————
● BpBinder:遠程Binder(Client端的Binder),這個類提供handle()方法返回指向Binder服務實現者的句柄,提供transact()方法來發(fā)送請求,BpXXX實現中會用到。
BpBinder類的成員變量mHandle是一個整數,它表示一個Client組件的句柄值,可以通過成員函數handle來獲取。每一個Client組件在Binder驅動程序中都對于一個Binder引用對象,而每一個Binder引用對象都有一個句柄值,其中,Client組件就是通過這個句柄值來和Binder驅動程序中的Binder引用對象建立對于關系的。
————————————
● BBinder:本地Binder(Server端的Binder),服務實現方的基類,提供了onTransact()接口來接收請求,和BpBinder的transact()方法對應。
BBinder類重要的成員函數onTransact,它負責分發(fā)于業(yè)務相關的進程間通信請求。事實上,與業(yè)務相關的進程間通信請求是由Binder本地對象類的子類,即Service組件來負責處理的。
而BpBinder的成員函數transact用來向運行在Server進程中的Service組件發(fā)送進程間通信請求,這是通過Binder驅動程序間接實現的。BpBinder類的成員函數transact會把BpBinder類的成員變量mHandle,以及進程間通信數據發(fā)送給Binder驅動程序,這樣Binder驅動程序就能根據這個句柄值來找到對應的Binder引用對象,繼而找到對應的Binder實體對象,最后就可以將進程間通信數據發(fā)送給對應的Service組件了(這里指實現BBinder的子類那些)
————————————
● ProcessState:代表了使用Binder的進程。它是一個單例類,一個進程只有一個實例。
● IPCThreadState:代表了使用Binder的線程,這個類封裝了與BInder驅動通信的邏輯。它是一個單例類,一個線程有一個實例。
○ transact():公開接口,供Proxy發(fā)送數據到驅動,并讀取返回結果。
○ sendReply():供Server端寫回請求的返回結果給驅動。
○ waitForResponse():發(fā)送請求后等待響應結果。
○ talkWithDriver():通過iotcl BINDER_WRITE_READ來與驅動通信。
○ writeTransactionData():寫入一次事務的數據。
○ executeCommand():處理binder_driver_return_protocol協議命令。
○ freeBuffer():通過BC_FREE_BUFFER命令釋放Buffer。
● Parcel:在Binder上傳遞數據的包裝器。
無論是BBinder類,還是BpBInder類,它們都是通過IPCThreadState類來和Binder驅動程序交互的,每一個使用了Binder進程間通信機制的進程都有一個Binder線程池,用來處理進程間通信請求。對于每一個Binder線程來說,它的內部都有一個IPCThreadState對象,我們可以通過IPCThreadState類的靜態(tài)成員函數self()來獲取,并且調用它的成員函數transact來和Binder驅動程序交互。在IPCThreadState類的成員函數transanct內部,與Binder驅動程序的交互操作又是通過調用函數talkWithDriver來實現的,它一方面負責向Binder驅動發(fā)送進程間通信請求,另一方面又負責接收來自Binder驅動程序的進程間通信請求。
IPCThreadState類有一個成員變量mProcess,它指向一個ProcessState對象。對于每一個使用了Binder進程間通信機制的進程來說,它的內部都有一個ProcessState對象,它負責初始化Binder設備,即打開設備文件/dev/binder,以及將設備文件/dev/binder映射到進程的地址空間。由于這個ProcessState對象在進程范圍內是唯一的,因此,Binder線程池中的每一個線程都可以通過它來和Binder驅動程序建立連接。
進程中的ProcessState對象可以通過ProcessState類的靜態(tài)成員函數self來獲取。第一次調用ProcessState類的靜態(tài)成員函數self時,Binder庫就會為進程創(chuàng)建一個ProcessState對象,并且調用函數open來打開設備文件/dev/binder,接著又調用函數mmap將它映射到(用戶)進程的地址空間,即請求Binder驅動程序為進程分配內核緩沖區(qū)。設備文件/dev/binder映射到(用戶)進程的地址空間后,得到的內核緩沖區(qū)的用戶地址就保存在其成員變量mVMStar中。
——————————————————
Kernel層
/bsp/kernel/kernel4.14/drivers/android/binder.c
原理分析
Binder通信流程
我們上面以及聊過,Binder的通信流程是先由Client發(fā)出,經過Binder Framework到達Kernel,再經過Kernel轉發(fā),最終通過Binder Framework到達Server。整體的通信流程有點像網絡通信。
我們以常見的啟動四大組件為例來描述Binder的通信流程,如下所示:
1、當我們在Activity里啟動一個Service,這個調用會經過層層傳遞到ActivityManagerProxy,然后ActivityManagerProxy會通過Binder發(fā)起跨進程調用。
2、接著Client就會向Binder Driver發(fā)起binder ioctl請求,在IPCTreadState::waitForResponse里執(zhí)行While循環(huán),在While循環(huán)中調用IPCThreadState::talkWithDriver()與驅動交互,然后Client線程等待驅動回復
binder驅動收到BC_TRANSACTION事件后的應答消息:
- BR_TRANSACTION_COMPLETE:對于oneway transaction(非阻塞通信、單向),當收到該消息,則完成了本次Binder通信
- BR_DEAD_REPLY:回復失敗,往往是線程或節(jié)點為空,則結束本次通信Binder
- BR_FAlLED_REPLY:回復失敗,往往是transaction出錯導致,則結束本次通信Binder
- BR_REPLY:對于非oneway transaction時,當收到該消息,則完整地完成本次Binder通信
3、上述命令除了BR_TRANSACTION_COMPLETE:其他回復Client端的進程都會接著調用IPCThreadState::executeCommand()處理驅動返回的命令。
4、對于Server端而言,會調用IPCThreadState::joinThreadPool循環(huán)執(zhí)行IPCThreadState::getAndExecuteCommand,最終調用ActivityManagerService.startService方法。如果需要回復Client會調用talkWithDriver與驅動通信
Client在與Service的通信過程按照是否需要Server返回數據可以分為兩種方式:
- 單向模式:不需要Server返回數據
- 雙向模式:需要Server返回數據
對以上的代碼做簡單分析:
binder_ioctl -> binder_ioctl_write_read -> binder_thread_write
```java
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
...
switch (cmd) {
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread)
...
}
——————————————————
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
...
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { //把用戶空間數據ubuf拷貝到bwr
ret = -EFAULT;
goto out;
...
if (bwr.write_size > 0) { //寫緩沖區(qū)有數據時,binder_thread_write
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
trace_binder_write_done(ret);
...
}
if (bwr.read_size > 0) { //讀緩沖區(qū)有數據時,binder_thread_read
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
binder_inner_proc_lock(proc);
...
}
...
//將內核數據bwr拷貝到用戶空間ubuf
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
}
Client端
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
if (reply) {
//等待響應
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
...
} else {
//oneway,則不需要等待 reply 的場景
err = waitForResponse(nullptr, nullptr);
}
—————————————————————
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
int32_t cmd;
int32_t err;
//在while循環(huán)中做下面的事情:
while (1) {
//循環(huán)中通過talkWithDriver與驅動通信
if ((err=talkWithDriver()) < NO_ERROR) break;
...
if (mIn.dataAvail() == 0) continue;
cmd = mIn.readInt32();
switch (cmd) {
... //循環(huán)中等待Binder驅動回復,執(zhí)行executeCommand
err = executeCommand(cmd);
...
}
}
...
return err;
}
—————————————————
status_t IPCThreadState::executeCommand(int32_t cmd)
{
...
switch ((uint32_t)cmd) { //switch判斷
case BR_ERROR:
result = mIn.readInt32();
break;
case BR_OK:
break;
case BR_ACQUIRE:
refs = (RefBase::weakref_type*)mIn.readPointer();
obj = (BBinder*)mIn.readPointer();
ALOG_ASSERT(refs->refBase() == obj,
"BR_ACQUIRE: object %p does not match cookie %p (expected %p)",
refs, obj, refs->refBase());
obj->incStrong(mProcess.get());
IF_LOG_REMOTEREFS() {
LOG_REMOTEREFS("BR_ACQUIRE from driver on %p", obj);
obj->printRefs();
}
mOut.writeInt32(BC_ACQUIRE_DONE);
mOut.writePointer((uintptr_t)refs);
mOut.writePointer((uintptr_t)obj);
break;
case BR_RELEASE:
refs = (RefBase::weakref_type*)mIn.readPointer();
obj = (BBinder*)mIn.readPointer();
...
Server端
void IPCThreadState::joinThreadPool(bool isMain)
{
...
do { //循環(huán)里面不斷的讀取命令數據,然后解析命令數據,并給出相應的響應。
processPendingDerefs();
// now get the next command to be processed, waiting if necessary
result = getAndExecuteCommand();
...
}
—————————————————————
status_t IPCThreadState::getAndExecuteCommand()
{
status_t result;
int32_t cmd;
result = talkWithDriver();
if (result >= NO_ERROR) {
size_t IN = mIn.dataAvail();
if (IN < sizeof(int32_t)) return result;
cmd = mIn.readInt32();
IF_LOG_COMMANDS() {
alog << "Processing top-level Command: "
<< getReturnString(cmd) << endl;
}
pthread_mutex_lock(&mProcess->mThreadCountLock);
mProcess->mExecutingThreadsCount++;
if (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads &&
mProcess->mStarvationStartTimeMs == 0) {
mProcess->mStarvationStartTimeMs = uptimeMillis();
}
pthread_mutex_unlock(&mProcess->mThreadCountLock);
//解析來自驅動的命令
result = executeCommand(cmd);
pthread_mutex_lock(&mProcess->mThreadCountLock);
mProcess->mExecutingThreadsCount--;
if (mProcess->mExecutingThreadsCount < mProcess->mMaxThreads &&
mProcess->mStarvationStartTimeMs != 0) {
int64_t starvationTimeMs = uptimeMillis() - mProcess->mStarvationStartTimeMs;
if (starvationTimeMs > 100) {
ALOGE("binder thread pool (%zu threads) starved for %" PRId64 " ms",
mProcess->mMaxThreads, starvationTimeMs);
}
mProcess->mStarvationStartTimeMs = 0;
}
pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
pthread_mutex_unlock(&mProcess->mThreadCountLock);
}
return result;
}
Binder驅動
這一塊可以理解為是用戶線程與Binder這個驅動設備交互的過程(用戶態(tài)切換到內核態(tài)的系統(tǒng)調用過程)
Binder是一個miscellaneous(混雜的,各種各樣的)類型的驅動,本身不對應任何硬件,所有的操作都在軟件層,Binder核心操作包含三個函數。
open:打開Binder設備。
mmap:進行內存映射。
ioctl:進行實際的通信操作。
binder_open
任何進程使用Binder之前都需要通過open(“/dev/binder”)打開Binder設備,驅動程序會為進程創(chuàng)建一個唯一的結構體binder_proc,該結構體持有進程的pid,與打開binder設備文件的進程相關聯,然后將該結構體加入全局hash隊列binder_procs中。該隊列保存了所有正在使用binder驅動的進程的binder_proc結構體,也就是說通過遍歷該隊列可以知道當前有多少個進程在使用binder進程間通信。
Binder驅動核心是維護一個binder_proc類型的雙向鏈表,里面記錄了servicemanager在內的所有service信息,當client去請求某個service時,binder驅動去binder_proc中查找相應的service返回給client,同時增加service的引用個數。
binder_procs里面有許多binder_proc:
對上面的基礎數據結構進行介紹
/bsp/kernel/kernel4.14/drivers/android/binder.c
**struct binder_proc:**每個進程調用open()打開binder驅動都會創(chuàng)建該結構體,用于管理IPC所需的各種信息。
struct binder_proc {
//掛載在全局binder_procs鏈表中的節(jié)點。
struct hlist_node proc_node;
//使用紅黑樹來保存使用Binder機制通信的進程的Binder線程池的線程、關聯binder_thread->rb_node
struct rb_root threads;
//binder_proc進程內的binder實體組成的紅黑樹(關聯binder_node->rb_node)
struct rb_root nodes;
//binder_proc進程內的binder引用組成的紅黑樹,以引用句柄的值來排序(關聯binder_ref->rb_node_desc)
struct rb_root refs_by_desc;
//binder_proc進程內的binder引用組成的紅黑樹,該引用以它對應的binder實體的地址來排序(關聯binder_ref->rb_node)
struct rb_root refs_by_node;
//空閑Binder線程會睡眠在wait描述的等待隊列中
struct list_head waiting_threads;
//進程pid
int pid;
//保存內核緩沖區(qū)在用戶空間的地址
struct vm_area_struct *vma;
//保存使用Binder機制通信的進程信息
struct task_struct *tsk;
struct files_struct *files;//打開文件結構體
//掛載在全局延遲工作項鏈表binder_deferred_list中的節(jié)點
struct hlist_node deferred_work_node;
//描述延遲工作項的具體類型
int deferred_work;
//表示要映射的物理內存在內核空間中的起始位置;
void *buffer;
//它表示的是內核使用的虛擬地址與進程使用的虛擬地址之間的差值
ptrdiff_t user_buffer_offset;
//指向被劃分為若干小塊的內核緩沖區(qū)
struct list_head buffers;
//指向沒有分配物理頁面的空閑小塊內核緩沖區(qū),用紅黑樹來組織,是為了分配合適的緩沖區(qū)能提高效率
struct rb_root free_buffers;
//指向已經分配了物理頁面正在使用的小塊內核緩沖區(qū)
struct rb_root allocated_buffers;
//保存當前可以用來保存異步事務數據的內核緩沖區(qū)的大小
size_t free_async_space;
//為內核緩沖區(qū)分配的物理頁面
struct page **pages;
//保存Binder驅動程序為進程分配的內核緩沖區(qū)的大小
size_t buffer_size;
//保存空閑內核緩沖區(qū)的大小
uint32_t buffer_free;
//進程待處理工作項隊列
struct list_head todo;
//空閑Binder線程會睡眠在wait描述的等待隊列中
wait_queue_head_t wait;
//統(tǒng)計進程接收到的進程間通信請求次數
struct binder_stats stats;
//死亡通知隊列
struct list_head delivered_death;
//保存Binder驅動程序最多可以主動請求進程注冊的線程數量
int max_threads;
//記錄請求注冊的線程個數
int requested_threads;
//記錄響應請求的線程個數
int requested_threads_started;
//保存進程當前空閑的Binder線程數目
int ready_threads;
//設置進程優(yōu)先級
long default_priority;
struct dentry *debugfs_entry;
};
進程打開設備文件/dev/binder之后,還必須調用函數mmap將它映射到進程的地址空間來,實際上是請求Binder驅動程序為它分配一看內核緩沖區(qū),以便用來接收進程間通信數據。Binder驅動程序為進程分配的內核緩沖區(qū)的大小保存在成員變量buffer_size中。**這些內核緩沖區(qū)有內核空間地址和用戶空間地址兩個地址,內核空間地址保存在成員變量void buffer;中,用戶空間地址保存在成員變量vm_area_struct vma;中,用戶空間程序通過用戶空間地址來訪問內核緩沖區(qū),內核緩沖區(qū)的內核空間地址和用戶空間地址之間相差一個固定值,保存在成員變量user_buffer_offset中。這樣就可以通過一個用戶空間地址或內核空間地址來計算出另外一個地址的大小。這兩個地址都是虛擬地址,對應的物理頁面保存在成員變量pages中,Binder驅動程序開始時只為內核緩沖區(qū)分配一個物理頁面。
Binder驅動為了方便管理內核緩沖區(qū),會將它劃分成若干小塊,使用binder_buffer來描述這些內核緩沖區(qū),并且按地址從小到大保存在成員變量buffers指向的鏈表中。當使用這些內核緩沖區(qū)時就會為其分配物理頁面,正在使用的內核緩沖區(qū)保存在allocated_buffers紅黑樹中,而空閑的內核緩沖區(qū)保存在free_buffers紅黑樹中。
結構體binder_proc的成員變量threads是一個紅黑樹的根節(jié)點,它以線程ID作為關鍵字來組織一個進程的Binder線程池。進程可以調用函數ioctl將一個線程注冊到Binder驅動程序中。
當進程接收到一個進程間通信請求時,Binder驅動程序就將該請求封裝成一個工作項,并且加入到進程的待處理隊列todo中,Binder線程池中的空閑Binder線程會睡眠在wait所描述的一個等待隊列中,當它們的宿主進程的待處理工作項隊列增加了新的工作項之后,Binder驅動程序會喚醒這些線程去處理新的工作項,同時線程優(yōu)先級被設置為default_priority值。
這里的threads、nodes這些其實是binder_thread、binder_node結構體的成員變量rb_node,而proc里面描述binder_thread等的數據類型是rb_root,也就是紅黑樹的節(jié)點。
**struct binder_node:**用來描述一個Binder實體對象。每一個Service組件(一個進程內有多個Service組件)在Binder驅動程序中都對應有一個Binder實體對象,用來描述它在內核的狀態(tài)。Binder驅動程序通過強弱引用計數來維護它們的生命周期。
struct binder_node {
//調試id
int debug_id;
//描述一個待處理的工作項
struct binder_work work;
union {
//掛載到宿主進程binder_proc的成員變量nodes紅黑樹的節(jié)點
struct rb_node rb_node;
//當宿主進程死亡,該binder實體對象將掛載到全局binder_dead_nodes鏈表中
struct hlist_node dead_node;
};
//指向該binder線程的宿主進程
struct binder_proc *proc;
//保存所有引用該binder實體對象的binder引用對象列表
struct hlist_head refs;
//binder實體對象的強引用計數
int internal_strong_refs;
int local_strong_refs;
unsigned has_strong_ref:1;
unsigned pending_strong_ref:1;
unsigned has_weak_ref:1;
unsigned pending_weak_ref:1;
//binder實體對象的弱引用計數
int local_weak_refs;
//指向用戶空間service組件內部的引用計數對象wekref_impl的地址
void __user *ptr;
//保存用戶空間的service組件地址
void __user *cookie;
//標示該binder實體對象是否正在處理一個異步事務
unsigned has_async_transaction:1;
//設置該binder實體對象是否可以接收包含有文件描述符的IPC數據
unsigned accept_fds:1;
//binder實體對象要求處理線程應具備的最小線程優(yōu)先級
unsigned min_priority:8;
//異步事務隊列
struct list_head async_todo;
};
宿主進程使用一個紅黑樹來維護它內部所有的Binder實體對象,而每一個Binder實體對象的成員變量rb_node就正好是這個紅黑樹中的一個節(jié)點。如果一個Binder實體對象的宿主進程已經死亡了,那么這個Binder實體對象就會通過它的成員變量dead_node保存在一個全局的hash列表中。
一個Binder實體對象可以被多個Binder引用對象引用的。
struct binder_ref:
struct binder_ref {
//調試id
int debug_id;
//掛載到宿主對象binder_proc的紅黑樹refs_by_desc中的節(jié)點,引用句柄來排序
struct rb_node rb_node_desc;
//掛載到宿主對象binder_proc的紅黑樹refs_by_node中的節(jié)點,binder實體的地址來排序
struct rb_node rb_node_node;
//掛載到Binder實體對象的refs鏈表中的節(jié)點
struct hlist_node node_entry;
//Binder引用對象的宿主進程binder_proc
struct binder_proc *proc;
//Binder引用對象所引用的Binder實體對象
struct binder_node *node;
//Binder引用對象的句柄值
uint32_t desc;
//強引用計數
int strong;
//弱引用計數
int weak;
//注冊死亡接收通知
struct binder_ref_death *death;
};
結構體binder_ref用來描述一個Binder引用對象。每一個Client組件在Binder驅動程序中都對應有一個Binder引用對象,用來描述它在內核中的狀態(tài)。Binder驅動程序通過強引用計數和弱引用計數技術來維護它們的生命周期。
成員變量 *node用來描述一個Binder引用對象所引用的Binder實體對象。前面在介紹結構體binder_node時提到,每一個Binder實體對象都有一個hash列表,用來保存那些引用了它的Binder引用對象,而這些Binder引用對象(可以理解為Cient端的binder_proc結構體里面的binder引用)的成員變量node_entry正好是這個hash列表的節(jié)點。
上面binder_node和binder_ref的關系如下圖所示:
**struct binder_buffer:**描述一個個內核緩存區(qū),它是用來在進程間傳輸數據的,可以被劃分交給某個事務使用
**binder_transaction:**描述一個進程間通信的過程,這個過程稱為一個事務。
struct binder_transaction {
int debug_id;
struct binder_work work; //描述這個事務的是做什么工作的
struct binder_thread *from;
struct binder_transaction *from_parent; //描述一個事務所依賴的另外一個事務
struct binder_proc *to_proc; //指向負責處理的進程
struct binder_thread *to_thread;//指向負責處理的線程
struct binder_transaction *to_parent; //目標線程下一個需要處理的事務
unsigned need_reply:1; //1表示這是同步通信,0表示異步
struct binder_buffer *buffer; //內核緩沖區(qū)
unsigned int code;
unsigned int flags;
struct binder_priority priority;
struct binder_priority saved_priority;
bool set_priority_called;
kuid_t sender_euid;
binder_uintptr_t security_ctx;
spinlock_t lock;
};
成員變量from_parent和to_parent分別描述一個事務所依賴的另外一個事務,以及目標線程下一個需要處理的事務。假設線程A發(fā)起了一個事務T1,需要由線程B來處理;線程B在處理事務T1時,又需要線程C先處理事務T2;線程C在處理事務T2時,又需要線程A先處理事務T3。這樣,事務T1就依賴于事務T2,而事務T2又依賴于事務T3,它們的關系如下:
T2->from_parent = T1
T3->from_parent = T2
對于線程A來說,它需要處理的事務有兩個,分別是T1和T3,它首先要處理事務T3,然后才能處理事務T1,因此,事務T1和T3的關系如下:
T3->to_parent = T1
考慮這樣一個情景:如果線程C在發(fā)起事務T3給線程A所屬的進程來處理時,Binder驅動程序選擇了該進程的另外一個線程D來處理該事務,這時候會出現什么情況呢?這時候線程A就會處于空閑等待狀態(tài),什么也不能做,因為它必須要等線程D處理完成事務T3后,它才可以繼續(xù)執(zhí)行事務T1。在這種情況下,與其讓線程A閑著,還不如把事務T3交給它來處理,這樣線程D就可以去處理其他事務,提高了進程的并發(fā)性。
現在,關鍵的問題又來了——Binder驅動程序在分發(fā)事務T3給目標進程處理時,它是如何知道線程A屬于目標進程,并且正在等待事務T3的處理結果的?當線程C在處理事務T2時,就會將事務T2放在其事務堆棧transaction_stack的最前端。這樣當線程C發(fā)起事務T3給線程A所屬的進程處理時,Binder驅動程序就可以沿著線程C的事務堆棧transaction_stack向下遍歷,即沿著事務T2的成員變量from_parent向下遍歷,最后就會發(fā)現事務T3的目標進程等于事務T1的源進程,并且事務T1是由線程A發(fā)起的,這時候它就知道線程A正在等待事務T3的處理結果了。
**binder_thread:**描述Binder線程池中的一個線程,內部也有todo隊列
**binder_work:**描述一個工作項,里面有枚舉類型值,比如這個工作項是屬于一個進程還是線程
struct binder_work {
struct list_head entry;
enum binder_work_type {
BINDER_WORK_TRANSACTION = 1,
BINDER_WORK_TRANSACTION_COMPLETE,
BINDER_WORK_RETURN_ERROR,
BINDER_WORK_NODE,
BINDER_WORK_DEAD_BINDER,
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
} type;
};
binder_mmap
在打開Binder設備之后,進程還會通過mmap(“mmap是一種內存映射文件的方法”)進行內存映射。
mmap()的作用主要有兩個:
● 申請一塊內存空間,用來接受Binder通信過程中的數據。
● 將這塊內存進行地址映射到用戶空間,以便將來訪問。
binder_ioctl
ioctl是一種專用于設備輸入輸出的系統(tǒng)調用,該調用傳入一個跟設備有關的請求碼,系統(tǒng)調用的功能完全取決于請求碼。
binder_ioctl()函數對應了ioctl系統(tǒng)的處理,它根據ioctl命令來確定進一步的處理邏輯,如下所示:
ioctl(文件描述符,ioctl 命令,數據類型);
1、打開Binder設備時的fd
2、具體的操作碼,通信協議的操作碼
3、存儲請求數據,類型是binder_write_read,內部封裝了一個binder_transaction_data結構體,該結構體包含了發(fā)出請求者的標識,請求的目標對象以及請求所需要的參數。
通信協議(通信命令),Binder協議可分為控制協議和驅動協議兩種:
1、Binder控制協議是進程通過ioctl(“/dev/binder”)與Binder設備進行通訊的協議。
操作碼有下面這些
2、Binder的驅動協議描述了對于Binder驅動的具體使用過程。驅動協議又可以分為兩類:
● binder_driver_command_protocol:描述了進程發(fā)送給Binder驅動的命令,以BC_開頭
● binder_driver_return_protocol:描述了Binder驅動發(fā)送給進程的命令,BR_開頭
binder_driver_command_protocol包含的命令,分別是:
binder_driver_return_protocol包含的命令,分別是:
我們前面提到的2個重要函數binder_thread_write和binder_thread_read就是對這些請求嗎進行處理
binder_thread_write(){
while (ptr < end && thread->return_error == BR_OK) {
...
get_user(cmd, (uint32_t __user *)ptr);//獲取 IPC 數據中的 Binder 協議(BC 碼)
...
switch (cmd) {
case BC_INCREFS: ...
case BC_ACQUIRE: ...
case BC_RELEASE: ...
case BC_DECREFS: ...
case BC_INCREFS_DONE: ...
case BC_ACQUIRE_DONE: ...
case BC_FREE_BUFFER: ... break;
case BC_TRANSACTION:
case BC_REPLY:
case BC_REGISTER_LOOPER: ...
case BC_ENTER_LOOPER: ...
case BC_EXIT_LOOPER: ...
case BC_REQUEST_DEATH_NOTIFICATION: ...
case BC_CLEAR_DEATH_NOTIFICATION: ...
case BC_DEAD_BINDER_DONE: ...
}
}
}
————————————
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
while (1) {
switch (w->type) { // 取出封裝的工作項binder_work的類型,再處理請求嗎
case BINDER_WORK_TRANSACTION: {} break;
case BINDER_WORK_RETURN_ERROR: {} break;
case BINDER_WORK_TRANSACTION_COMPLETE: {
binder_inner_proc_unlock(proc);
cmd = BR_TRANSACTION_COMPLETE;
...
} break;
case BINDER_WORK_NODE: {
...
ret = binder_put_node_cmd(
proc, thread, &ptr, node_ptr,
node_cookie, node_debug_id,
BR_INCREFS, "BR_INCREFS");
...
} break;
case BINDER_WORK_DEAD_BINDER:
case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
...
cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;
}break;
...
}
}
}
Binder線程池
Binder線程池:每個進程在啟動時會創(chuàng)建一個binder線程池(比如app進程、systemserver),并向其中注冊一個Binder線程
在Zygote進程中初始化該APP進程的時候,會調用到Native層的app_main.cpp中的onZygoteInit()。
frameworks/base/cmds/app_process/app_main.cpp:
virtual void onZygoteInit()
{
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool(); //開啟線程池
}
為當前進程創(chuàng)建ProcessState的時候,通過mMaxThreads傳入了Binder驅動可創(chuàng)建的最大Binder線程數,默認為15個。
frameworks/native/libs/binder/ProcessState.cpp
#define DEFAULT_MAX_BINDER_THREADS 15
...
ProcessState::ProcessState(const char *driver)
: mDriverName(String8(driver))
, mDriverFD(open_driver(driver))
, mVMStart(MAP_FAILED)
...
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
...
{
...
}
之后Server進程也可以向binder線程池注冊新的線程,或者Binder驅動在探測到沒有足夠的binder線程來處理Client請求時會主動向Server進程要求注冊新的的binder線程去處理請求
下面的代碼為Binder驅動在探測到沒有足夠的binder線程來處理Client請求時會主動向Server進程要求注冊新的的binder線程去處理請求的情況
frameworks/source/drivers/android/binder.c
done:
*consumed = ptr - buffer;
binder_inner_proc_lock(proc);
//當proc->requested_threads == 0: 正在注冊的已經為0
//list_empty(&thread->proc->waiting_threads): 空閑的Binder線程隊列為0,表示都在處理請求中
//.proc->requested_threads_started < proc->max_threads:已經啟動的用來處理線程數 < 設定最大值數
if (proc->requested_threads == 0 &&
list_empty(&thread->proc->waiting_threads) &&
proc->requested_threads_started < proc->max_threads &&
(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
/*spawn a new thread if we leave this out */) {
proc->requested_threads++; //請求注冊+1,就是代表注冊一個Binder線程用來處理請求
binder_inner_proc_unlock(proc);
binder_debug(BINDER_DEBUG_THREADS,
"%d:%d BR_SPAWN_LOOPER\n",
proc->pid, thread->pid);
//發(fā)送一個創(chuàng)建線程的請求給應用程序
if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
return -EFAULT;
binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
} else
binder_inner_proc_unlock(proc);
return 0;
}
上面注冊成功后的Binder線程并不是馬上變?yōu)榱藃equested_threads_started。
當某個Client發(fā)送請求過來,進行寫操作時,通過binder_ioctl再到binder_thread_write,我們這邊Server端的進程會執(zhí)行proc->requested_threads–-。如下:
static int binder_thread_write(struct binder_proc *proc,struct binder_thread *thread,binder_uintptr_t binder_buffer, size_t size,binder_size_t *consumed)
case BC_REGISTER_LOOPER:
/*注冊后未使用的線程數-1*/
proc->requested_threads--;
/*已經啟動的線程數目+1*/
proc->requested_threads_started++;
表示新的線程已經進入了循環(huán)體。
對于底層Binder驅動,通過binder_procs鏈表記錄所有創(chuàng)建的binder_proc結構體,binder驅動層的每一個 binder_proc結構體都與用戶空間的一個用于binder通信的進程一一對應,且每個進程有且只有一ProcessState 對象,這是通過單例模式來保證的。
在每個進程中可以有很多個線程,每個線程對應一個IPCThreadState對象,IPCThreadState對象也是單例模式,即一個線程對應一個IPCThreadState對象,在Binder驅動層與之相對應的結構Binder_thread結構體。在 binder_proc結構體中通過成員變量rb_root threads,來記錄當前進程內所有的binder_thread。
Binder實體對象和Binder本地對象之間
Binder本地對象是一個類型為BBinder的對象,它是在用戶空間中創(chuàng)建的,并且運行在Server進程中。Binder本地對象一方面會被運行在Server進程中的其他對象引用,另一方面也會被Binder驅動程序中的Binder實體對象引用。由于BBinder類繼承了RefBase類,因此,Server進程中的其他對象可以簡單地通過智能指針來引用這些Binder本地對象,以便可以控制它們的生命周期。
由于Binder驅動程序中的Binder實體對象是運行在內核空間的,它不能夠通過智能指針來引用運行在用戶空間的Binder本地對象,因此,Binder驅動程序就需要和Server進程約定一套規(guī)則來維護它們的引用計數,避免它們在還被Binder實體對象引用的情況下銷毀。
Server進程將一個Binder本地對象注冊到Sevice Manager時,Binder驅動程序就會為它創(chuàng)建一個Binder實體對象。接下來,當Client進程通過Service Manager來查詢一個Binder本地對象的代理對象接口時,Binder驅動程序就會為它所對應的Binder實體對象創(chuàng)建一個Binder引用對象(指某個Client端的)。
接著再使用BR_INCREFS和BR_ACQUIRE協議來通知對應的Server進程增加對應的Binder本地對象的弱引用計數和強引用計數。保證Binder代理對象在引用一個Binder本地對象的時候,本地對象不會被銷毀
當沒有任何Binder代理對象引用一個Binder本地對象時,Binder驅動程序就會使用BR_DECREFS和BR_RELEASE協議來通知對應的Server進程減少對應的Binder本地對象的弱引用計數和強引用計數。
下面的情況是由Server進程要斷開連接的情況:
總結來說,Binder驅動程序就是通過BR_INCREFS、BR_ACQUIRE、BR_DECREFS和BR_RELEASE協議來引用運行在Server進程中的Binder本地對象的,相關的代碼實現在binder.c文件函數binder_thread_read中
當Binder驅動程序要和目標進程或者目標線程通信時,它就會把一個工作項加入到它的todo隊列中。目標進程或者目標線程會不斷地調用Binder驅動程序中的函數binder_thread_read()來檢查它的todo隊列中有沒有新的工作項。如果有,目標進程或者目標線程就會將它取出來,并且返回到用戶空間去處理。
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
......
while (1) {
uint32_t cmd;
struct binder_transaction_data_secctx tr;
struct binder_transaction_data *trd = &tr.transaction_data;
struct binder_work *w = NULL;
struct list_head *list = NULL;
struct binder_transaction *t = NULL;
struct binder_thread *t_from;
size_t trsize = sizeof(*trd);
...
case BINDER_WORK_NODE: { //這個即是為一個BINDER_WORK_NODE類型的工作項
struct binder_node *node = container_of(w, struct binder_node, work);
int strong, weak;
binder_uintptr_t node_ptr = node->ptr;
binder_uintptr_t node_cookie = node->cookie;
int node_debug_id = node->debug_id;
int has_weak_ref;
int has_strong_ref;
void __user *orig_ptr = ptr;
...
if (weak && !has_weak_ref) {
node->has_weak_ref = 1;
node->pending_weak_ref = 1;
node->local_weak_refs++;
}
if (strong && !has_strong_ref) {
node->has_strong_ref = 1;
node->pending_strong_ref = 1;
node->local_strong_refs++;
}
if (!strong && has_strong_ref)
node->has_strong_ref = 0;
if (!weak && has_weak_ref)
node->has_weak_ref = 0;
...
if (weak && !has_weak_ref)
ret = binder_put_node_cmd(
proc, thread, &ptr, node_ptr,
node_cookie, node_debug_id,
BR_INCREFS, "BR_INCREFS");
if (!ret && strong && !has_strong_ref)
ret = binder_put_node_cmd(
proc, thread, &ptr, node_ptr,
node_cookie, node_debug_id,
BR_ACQUIRE, "BR_ACQUIRE");
if (!ret && !strong && has_strong_ref)
ret = binder_put_node_cmd(
proc, thread, &ptr, node_ptr,
node_cookie, node_debug_id,
BR_RELEASE, "BR_RELEASE");
if (!ret && !weak && has_weak_ref)
ret = binder_put_node_cmd(
proc, thread, &ptr, node_ptr,
node_cookie, node_debug_id,
BR_DECREFS, "BR_DECREFS");
......
return 0;
}
如果Binder驅動程序已經請求運行Server進程中的一個Binder本地對象為它內部的一個Binder實體對象增加了強引用計數和弱引用計數,那么Binder驅動程序就會將該Binder實體對象的成員變量has_strong_ref和has_weak_ref的值設置為1;否則,就設置為0。
從31-69行的if語句就根據變量strong和weak的值,以及目標Binder實體對象的成員變量has_strong_ref和has_weak_ref的值,來判斷通知目標進程增加還是減少一個對應的Binder本地對象的強引用計數或者弱引用計數,判斷規(guī)則如下所示。
(1)如果變量weak的值等于1(表示有一個Binder引用對象引用我了),但我們Binder實體對象的成員變量has_weak_ref的值是等于0的,那么就說明該Binder實體對象已經引用了一個Binder本地對象,但是還沒有增加它的弱引用計數。因此,第32行到第34行和第50到53行代碼就使用BR_INCREFS協議來請求增加對應的Binder本地對象的弱引用計數以及將協議內容寫入到由Server進程所提供的一個用戶空間緩沖區(qū)(寫入的不是數據,已經映射好了),然后返回到Server進程的用戶空間。
Server進程是通過Binder庫提供的IPCThreadState接口來處理Binder驅動程序發(fā)送給它的協議的,具體來說,就是在IPCThreadState類的成員函數executeCommand中處理BR_INCREFS、BR_ACQUIRE、BR_DECREFS和BR_RELEASE這四個協議,如下所示。
status_t IPCThreadState::executeCommand(int32_t cmd)
{
BBinder* obj;
RefBase::weakref_type* refs;
status_t result = NO_ERROR;
switch ((uint32_t)cmd) {
...
case BR_ACQUIRE:
refs = (RefBase::weakref_type*)mIn.readPointer();
obj = (BBinder*)mIn.readPointer();
...
obj->incStrong(mProcess.get());
...
mOut.writeInt32(BC_ACQUIRE_DONE);
mOut.writePointer((uintptr_t)refs);
mOut.writePointer((uintptr_t)obj);
break;
case BR_RELEASE:
refs = (RefBase::weakref_type*)mIn.readPointer();
obj = (BBinder*)mIn.readPointer();
...
mPendingStrongDerefs.push(obj);
break;
case BR_INCREFS:
refs = (RefBase::weakref_type*)mIn.readPointer();
obj = (BBinder*)mIn.readPointer();
refs->incWeak(mProcess.get());
mOut.writeInt32(BC_INCREFS_DONE);
mOut.writePointer((uintptr_t)refs);
mOut.writePointer((uintptr_t)obj);
break;
case BR_DECREFS:
refs = (RefBase::weakref_type*)mIn.readPointer();
obj = (BBinder*)mIn.readPointer();
...
mPendingWeakDerefs.push(refs);
break;
...
}
......
return result;
}
當Binder驅動程序請求Server進程增加或者減少一個Binder本地對象的強引用計數或者弱引用計數時,會將該Binder本地對象內部的一個弱引用計數對象(weakref_type),以及該Binder本地對象的地址傳遞過來,因此,IPCThreadState類的成員函數executeCommand就可以知道要修改的是哪一個Binder本地對象的引用計數了。
Binder驅動程序傳遞給Server進程的數據保存在IPCThreadState類的成員變量mln中,第10行和第11行、第20行和第21行、第27行和第28行,以及第36行和第37行就分別從它里面取出上述兩個地址,并且分別將它轉換為一個weakref_type對象refs和一個BBinder對象obj。
對于BR_ACQUIRE和BR_INCREFS協議,Server進程會馬上增加目標Binder本地對象的強引用計數和弱引用計數,如第15行和第30行代碼所示,并且使用BC_ACQUIRE_DONE和BC_INCREFS_DONE協議來通知Binder驅動程序,它已經增加相應的Binder本地對象的引用計數了。
Bindeer引用對象與Binder實體對象之間
實體對像和引用對象在同一個層次,直接調用內部函數即可實現引用計數
Binder代理對象與Binder引用對象之間
需要協議,屬于跨層次
Binder對象死亡通知機制
Client進程和Server進程內部錯誤而異常退出,會導致運行在它們里面的Binder代理對象和Binder本地對象就會意外死亡。而對象死亡通知機制可以監(jiān)控到Binder本地對象的死亡事件,然后通知那些引用了它的Binder代理對象。
死亡通知機制中,首先是Binder代理對象將一個死亡接收通知注冊到Binder驅動程序中,然后當Binder驅動程序監(jiān)控到它引用的Binder本地對象死亡時,Binder驅動程序就會向它發(fā)送一個死亡通知。當然,這個通知也是可以注銷的。
注冊死亡接受通知
Binder庫定義了死亡通知接收者必須要繼承的基類DeathRecipient,它的實現如下所示。自定義的死亡通知接收者必須要重寫父類DeathRecipient的成員函數binderDied。當Binder驅動程序通知一個Binder代理對象它所引用的Binder本地對象已經死亡時,就會調用它所指定的死亡通知接收者的成員函數binderDied()
class IBinder : public virtual RefBase
{
public:
...
class DeathRecipient : public virtual RefBase
{
public:
virtual void binderDied(const wp<IBinder>& who) = 0;
};
...
};
Service Manager
ServiceManager是Binder進程間通信機制的核心組件之一,它扮演著Binder進程間通信機制上下文管理者(Context Manager)的角色,同時負責管理系統(tǒng)中的Service組件,并且向Client組件提供獲取Service代理對象的服務。
Service Manager運行在一個獨立的進程中,因此,Service組件和Client組件也需要通過進程間通信機制來和它交互。Client組件在使用Service組件提供的服務之前,需要通過Service Manager來獲取Service組件的代理對象,由于Service Manager也是一個Service組件,所以,其他Service組件和Client組件也需要先獲得Service Manager的代理對象。
然后Server組件就可以向ServiceManager的mNameToService集合中緩存注冊的服務,Client端需要先拿到Sevice Manager的代理對象proxy后,才能通過ServiceManager到mNameToService集合中查詢所需服務并返回Server端的代理
Service Manager初始化
在Android的源碼中主要有兩種不同的方式來注冊系統(tǒng)服務:
● ServiceManager.addService()
● SystemServiceManager.startService()
獲?。?br> ● context.getSystemService:context.getSystemService 方法用于獲取緩存在 ContextImpl 中的系統(tǒng)服務 Manager。
● ServiceManager.getService:從ServiceManager的mNameToService集合中獲取緩存注冊的服務
入口:
frameworks/native/cmds/servermanager/main.cpp
int main(int argc, char** argv) {
if (argc > 2) {
LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
}
const char* driver = argc == 2 ? argv[1] : "/dev/binder";
sp<ProcessState> ps = ProcessState::initWithDriver(driver);
//servicemanager是單線程模型,所以限定了Binder線程池線程數為0,也不會去開啟線程池
ps->setThreadPoolMaxThreadCount(0);
ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
LOG(ERROR) << "Could not self register servicemanager";
}
IPCThreadState::self()->setTheContextObject(manager);
ps->becomeContextManager(nullptr, nullptr);
sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
BinderCallback::setupTo(looper);
ClientCallbackCallback::setupTo(looper, manager);
//進入無限循環(huán),處理client端發(fā)來的請求
while(true) {
looper->pollAll(-1);
}
// should not be reached
return EXIT_FAILURE;
}
ps->becomeContextManager(nullptr, nullptr);
frameworks/native/libs/binder/ProcessState.cpp
bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData)
{
AutoMutex _l(mLock);
mBinderContextCheckFunc = checkFunc;
mBinderContextUserData = userData;
flat_binder_object obj {
.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
};
int result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj);
// fallback to original method
if (result != 0) {
android_errorWriteLog(0x534e4554, "121035042");
int dummy = 0;
result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
}
if (result == -1) {
mBinderContextCheckFunc = nullptr;
mBinderContextUserData = nullptr;
ALOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno));
}
return result == 0;
}
獲取服務
frameworks/native/cmds/servicemanager/ServiceManager.cpp
ServiceManager::getService
Status ServiceManager::getService(const std::string& name, sp<IBinder>* outBinder) {
*outBinder = tryGetService(name, true);
// returns ok regardless of result for legacy reasons
return Status::ok();
}
————————
sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfNotFound) {
auto ctx = mAccess->getCallingContext();
sp<IBinder> out;
Service* service = nullptr;
//mNameToService.find(name);查找緩存服務
if (auto it = mNameToService.find(name); it != mNameToService.end()) {
service = &(it->second);
...
out = service->binder;
}
...
//沒有找到緩存的服務,這里去開啟
if (!out && startIfNotFound) {
tryStartService(name);
}
...
return out;
}
frameworks/native/cmds/servicemanager/ServiceManager.h
mNameToService是一個Map結構
using ServiceMap = std::map<std::string, Service>;
...
ServiceMap mNameToService;
Binder進程通信機制的JAVA接口
從我們Java層調用為起點,說一說大致過程。首先實現方式有兩種,我這里選擇用aidl方式
從我們Java層調用為起點,說一說大致過程
一、Java接口的定義和解析
二、Java服務的調用過程。
(1)Java接口的定義和解析,因為我是在之前練習dagger的項目改寫的,所以這里大家不要在意daager這個包名
通過Android接口描述語言(Android lnterface DefinitionLanguage,AIDL)來定義Java服務接口。AIDL是一種Binder進程間通信接口的描述語言,通過它來定義的Java服務接口具有執(zhí)行Binder進程間通信的能力。
定義文件IRemoteService.aidl中
interface IRemoteService {
int getPid();
MyData getMyData();
}
編譯該文件后,out目錄會生成一個中間文件IRemoteService.java
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.example.daagerapplication;
// Declare any non-default types here with import statements
public interface IRemoteService extends android.os.IInterface
{
///
/** Default implementation for IRemoteService. */
public static class Default implements com.example.daagerapplication.IRemoteService
{
@Override public int getPid() throws android.os.RemoteException
{
return 0;
}
@Override public com.example.daagerapplication.MyData getMyData() throws android.os.RemoteException
{
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
///
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.daagerapplication.IRemoteService
{
private static final java.lang.String DESCRIPTOR = "com.example.daagerapplication.IRemoteService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.daagerapplication.IRemoteService interface,
* generating a proxy if needed.
*/
public static com.example.daagerapplication.IRemoteService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.daagerapplication.IRemoteService))) {
return ((com.example.daagerapplication.IRemoteService)iin);
}
return new com.example.daagerapplication.IRemoteService.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getPid:
{
data.enforceInterface(descriptor);
int _result = this.getPid();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getMyData:
{
data.enforceInterface(descriptor);
com.example.daagerapplication.MyData _result = this.getMyData();
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
**********************************
private static class Proxy implements com.example.daagerapplication.IRemoteService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public int getPid() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getPid();
}
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public com.example.daagerapplication.MyData getMyData() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.daagerapplication.MyData _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getMyData, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getMyData();
}
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.example.daagerapplication.MyData.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.example.daagerapplication.IRemoteService sDefaultImpl;
}
**********************************
static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getMyData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public static boolean setDefaultImpl(com.example.daagerapplication.IRemoteService impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.daagerapplication.IRemoteService getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
///
public int getPid() throws android.os.RemoteException;
public com.example.daagerapplication.MyData getMyData() throws android.os.RemoteException;
}
文件會生成一個Java抽象類IRemoteService.Stub和一個Java類IRemoteService.Stub.Proxy
抽象類IRemoteService.Stub用來描述一個Java服務,而抽象類IRemoteService.Stub.Proxy類用來描述一個Java服務代理對象。
IRemoteService.Stub類的靜態(tài)成員函數asInterface通常用來將一個Java服務代理對象,即一個BinderProxy對象(從ServiceManager拿到的)封裝成一個IRemoteService.Stub.Proxy對象。
IRemoteService.Stub類的成員函數onTransact是用來接收和分發(fā)進程間通信請求的。
IRemoteService.Stub.Proxy類內部有一個成員變量mRemote,它指向的是一個Java服務代理對象,即一個BinderProxy對象,用來向一個實現了IRemoteService接口的Java服務發(fā)送進程間通信請求
(2)Java服務的調用過程。
獲取了Java代理對象之后,就可以調用這個實現了IRemoteService接口的Java代理對象的成員函數getPid();
和getMyData();
public class MainActivity extends AppCompatActivity {
...
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteService = IRemoteService.Stub.asInterface(service);
String pidInfo = null;
try {
MyData myData = mRemoteService.getMyData();
pidInfo = "pid="+ mRemoteService.getPid() +
", data1 = "+ myData.getData1() +
", data2="+ myData.getData2();
} catch (RemoteException e) {
e.printStackTrace();
}
Log.i(TAG, "[ClientActivity] onServiceConnected "+pidInfo);
mCallBackTv.setText(pidInfo);
Toast.makeText(MainActivity.this, "R.string.remote_service_connecte d", Toast.LENGTH_SHORT).show();
}
...
}
}
mRemoteService.getMyData() 過程
public interface IRemoteService extends android.os.IInterface
{
...
@Override public com.example.daagerapplication.MyData getMyData() throws android.os.RemoteException
{
//2個包,一個帶數據過去,一個收回復的數據
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.daagerapplication.MyData _result;
try {
//首先將通信數據寫入到一個Parcel對象_data中,然后mRemote.transact發(fā)起進程間通信
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getMyData, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getMyData();
}
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.example.daagerapplication.MyData.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
mRemote.transact的請求最后是JavaBBinder的Binder本地對象的成員函數onTransact進行處理
JavaBBinder類的成員變量mObject指向的是一個Java服務,這個java服務就是我們的RemoteService
frameworks/base/core/jni
...
class JavaBBinder : public BBinder
virtual status_t onTransact(
uint32_t code, const Parcel6 data, Parcel* reply, uint32_t flags = 0)
{
...
JNIEnv* env = javavm_to_jnienv(mVM);
jboolean rea = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
code, (int32_t) &data, (int32_t)reply, flags);
...
return res I= JNI_FALSE ? NO_ ERROR : UNKNOWN_TRANSAOTION;
}
...
第8行代碼轉給了Binder類的execTransact,因為子類沒有重寫這個方法,所以執(zhí)行的是父類
frameworks/base/core/java/android/os
public class Binder implements IBinder {
...
// Entry point from android_util_Binder.cpp's onTransact
@UnsupportedAppUsage
private boolean execTransact(int code, long dataObj, long replyObj,
...
return execTransactInternal(code, dataObj, replyObj, flags, callingUid);
...
}
private boolean execTransactInternal(int code, long dataObj, long replyObj, int flags,
int callingUid){
...
res = onTransact(code, data, reply, flags);
...
}
會轉給由子類重寫的成員函數onTransact,實際上就是IRemoteService.Stub類的成員函數onTransact,因為public static abstract class Stub extends android.os.Binder
case TRANSACTION_getMyData:
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
...
case TRANSACTION_getPid:
{
data.enforceInterface(descriptor);
int _result = this.getPid();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getMyData:
{
data.enforceInterface(descriptor);
com.example.daagerapplication.MyData _result = this.getMyData();
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
com.example.daagerapplication.MyData _result = this.getMyData();最后轉給實際服務RemoteService執(zhí)行
package com.example.daagerapplication;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
//寫法2:public class RemoteService 直接 extends IRemoteService.Stub,這里測試用extends Service方式也可以
//寫法3:public class RemoteService extends systemServer的方式
public class RemoteService extends Service {
/** * 實現 IRemoteService.aidl 中定義的方法 */
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
@Override
public int getPid() throws RemoteException {
Log.i(TAG,"[RemoteService] getPid()="+android.os.Process.myPid());
return android.os.Process.myPid();
}
@Override
public MyData getMyData() throws RemoteException {
Log.i(TAG,"[RemoteService] getMyData() "+ mMyData.toString());
return mMyData;
}
/**此處可用于權限攔截**/
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
return super.onTransact(code, data, reply, flags);
}
};
/** * 初始化 MyData 數據 **/
private void initMyData() {
mMyData = new MyData();
mMyData.setData1(10);
mMyData.setData2(20);
}
}
通信常見異常
1、Binder leak,Binder內存泄漏,binder_buffer大概為4M(應用空間限制為1M,所以和4M其實關系不大),如果在通信過程中數據沒有有效的釋放,會造成空間越來越少。這個釋放動作由Binder驅動做,一般很少出錯
2、binder某個進程線程池處理線程達到上限(16個),可能會導致ANR。解決方法找log其他地方看是誰一直在消耗binder或者是有死鎖發(fā)生
3、Binder線程池死鎖,分析trace文件
4、Client端異常比如傳輸數據量超過1M,Service端異常比如提供的方法不可用,解決方法改代碼
自問自答:
binder_procs是誰創(chuàng)建的?
答:Binder驅動初始化的時候會創(chuàng)建這個binder_procs鏈表。
可以不通過ServiceManager進行Binder跨進程通信嘛?
答:可以的,不是所有的Binder都需要注冊給ServiceManager的(比如我上面寫的例子)。Server端可以通過已經建立的Binder連接將創(chuàng)建的Binder實體傳給Client,當然這條已經建立的Binder連接必須是通過實名Binder實現。由于這個Binder沒有向ServiceManager注冊名字,所以是匿名Binder。Client將會收到這個匿名Binder的引用,通過這個引用向位于Server中的實體發(fā)送請求。匿名Binder為通信雙方建立一條私密通道,只要Server沒有把匿名Binder發(fā)給別的進程,別的進程就無法通過窮舉或猜測等任何方式獲得該Binder的引用,向該Binder發(fā)送請求。文章來源:http://www.zghlxwxcb.cn/news/detail-434863.html
ServiceManager是單線程的,為什么它不擔心處理不過來的問題或者說它為什么是被設計為單線程?而不是像其他進程有Binder線程池?
答:請求量較少的原因吧,單線程綽綽有余。文章來源地址http://www.zghlxwxcb.cn/news/detail-434863.html
到了這里,關于Binder通信原理的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!