Android Binder IPC分析
1 . binder 通訊概述
binder 通訊是一種 client-server 的通訊結構,
1. 從表面上來看,是 client 通過獲得一個 server 的代理介面,對 server 進行直接呼叫;
2. 實際上,代理介面中定義的方法與 server 中定義的方法是一一對應的;
3.client 呼叫某個代理介面中的方法時,代理介面的方法會將 client 傳遞的引數打包成為 Parcel 物件;
4. 代理介面將該 Parcel 傳送給核心中的 binder driver.
5.server 會讀取 binder driver 中的請求資料,如果是傳送給自己的,解包 Parcel 物件,處理並將結果返回;
6. 整個的呼叫過程是一個同步過程,在 server 處理的時候, client 會 block 住。
2 . service manager
Service Manager 是一個 linux 級的程式 , 顧名思義,就是 service 的管理器。這裡的 service 是什麼概念呢?這裡的service 的概念和 init 過程中 init.rc 中的 service 是不同, init.rc 中的 service 是都是 linux 程式,但是這裡的 service 它並不一定是一個程式,也就是說可能一個或多個 service 屬於同一個 linux 程式。在這篇文章中不加特殊說明均指 android native 端的 service 。
任何 service 在被使用之前,均要向 SM(Service Manager) 註冊,同時客戶端需要訪問某個 service 時,應該首先向SM 查詢是否存在該服務。如果 SM 存在這個 service ,那麼會將該 service 的 handle 返回給 client , handle 是每個service 的唯一識別符號。
SM 的入口函式在 service_manager.c 中,下面是 SM 的程式碼部分
int main(int argc, char **argv)
{
struct binder_state *bs;
void *svcmgr = BINDER_SERVICE_MANAGER;
bs = binder_open(128*1024);
if (binder_become_context_manager(bs)) {
LOGE("cannot become context manager (%s)/n", strerror(errno));
return -1;
}
svcmgr_handle = svcmgr;
binder_loop(bs, svcmgr_handler);
return 0;
}
這個程式的主要工作如下:
1. 初始化 binder ,開啟 /dev/binder 裝置;在記憶體中為 binder 對映 128K 位元組空間;
2. 指定 SM 對應的代理 binder 的 handle 為 0 ,當 client 嘗試與 SM 通訊時,需要建立一個 handle 為 0 的代理 binder,這裡的代理 binder 其實就是第一節中描述的那個代理介面;
3. 通知 binder driver(BD) 使 SM 成為 BD 的 context
manager ;
4. 維護一個死迴圈,在這個死迴圈中,不停地去讀核心中 binder driver ,檢視是否有可讀的內容;即是否有對 service的操作要求 , 如果有,則呼叫 svcmgr_handler 回撥來處理請求的操作。
5.SM 維護了一個 svclist 列表來儲存 service 的資訊。
這裡需要宣告一下,當 service 在向 SM 註冊時,該 service 就是一個 client ,而 SM 則作為了 server 。而某個程式需要與 service 通訊時,此時這個程式為 client , service 才作為 server 。因此 service 不一定為 server ,有時它也是作為 client 存在的。
由於下面幾節會介紹一些與 binder 通訊相關的幾個概念,所以將 SM 的功能介紹放在了後面的部分來講。
應用和 service 之間的通訊會涉及到 2 次 binder 通訊。
1. 應用向 SM 查詢 service 是否存在,如果存在獲得該 service 的代理 binder ,此為一次 binder 通訊;
2. 應用通過代理 binder 呼叫 service 的方法,此為第二次 binder 通訊。
3 . ProcessState
ProcessState 是以單例模式設計的。每個程式在使用 binder 機制通訊時,均需要維護一個 ProcessState 例項來描述當前程式在 binder 通訊時的 binder 狀態。
ProcessState 有如下 2 個主要功能:
1. 建立一個 thread, 該執行緒負責與核心中的 binder 模組進行通訊,稱該執行緒為 Pool thread ;
2. 為指定的 handle 建立一個 BpBinder 物件,並管理該程式中所有的 BpBinder 物件。
3.1 Pool thread
在 Binder IPC 中,所有程式均會啟動一個 thread 來負責與 BD 來直接通訊,也就是不停的讀寫 BD ,這個執行緒的實現主體是一個 IPCThreadState 物件,下面會介紹這個型別。
下面是 Pool thread 的啟動方式:
ProcessState::self()->startThreadPool();
3.2 BpBinder 獲取
BpBinder 主要功能是負責 client 向 BD 傳送呼叫請求的資料。它是 client 端 binder 通訊的核心物件,通過呼叫transact 函式向 BD 傳送呼叫請求的資料,它的建構函式如下:
BpBinder(int32_t handle);
通過 BpBinder 的建構函式發現, BpBinder 會將當前通訊中 server 的 handle 記錄下來,當有資料傳送時,會通知 BD資料的傳送目標。
ProcessState 通過如下方式來獲取 BpBinder 物件:
ProcessState::self()->getContextObject(handle);
在這個過程中, ProcessState 會維護一個 BpBinder 的 vector mHandleToObject ,每當 ProcessState 建立一個BpBinder 的例項時,回去查詢 mHandleToObject ,如果對應的 handle 已經有 binder 指標,那麼不再建立,否則建立binder 並插入到 mHandleToObject 中。
ProcessState 建立的 BpBinder 例項,一般情況下會作為引數構建一個 client 端的代理介面,這個代理介面的形式為BpINTERFACE , 例如在與 SM 通訊時, client 會建立一個代理介面 BpServiceManager .
4 . IPCThreadState
IPCThreadState 也是以單例模式設計的。由於每個程式只維護了一個 ProcessState 例項,同時 ProcessState 只啟動一個 Pool
thread ,也就是說每一個程式只會啟動一個 Pool thread ,因此每個程式則只需要一個 IPCThreadState 即可。
Pool thread 的實際內容則為:
IPCThreadState::self()->joinThreadPool();
ProcessState 中有 2 個 Parcel 成員, mIn 和 mOut , Pool thread 會不停的查詢 BD 中是否有資料可讀,如果有將其讀出並儲存到 mIn ,同時不停的檢查 mOut 是否有資料需要向 BD 傳送,如果有,則將其內容寫入到 BD 中,總而言之,從BD 中讀出的資料儲存到 mIn ,待寫入到 BD 中的資料儲存在了 mOut 中。
ProcessState 中生成的 BpBinder 例項通過呼叫 IPCThreadState 的 transact 函式來向 mOut 中寫入資料,這樣的話這個 binder IPC 過程的 client 端的呼叫請求的傳送過程就明瞭了 。
IPCThreadState 有兩個重要的函式, talkWithDriver 函式負責從 BD 讀寫資料, executeCommand 函式負責解析並執行 mIn 中的資料。
5. 主要基類
5.1 基類 IInterface
為 server 端提供介面,它的子類宣告瞭 service 能夠實現的所有的方法;
5.2 基類 IBinder
BBinder 與 BpBinder 均為 IBinder 的子類,因此可以看出 IBinder 定義了 binder
IPC 的通訊協議, BBinder 與 BpBinder在這個協議框架內進行的收和發操作,構建了基本的 binder
IPC 機制。
5.3 基類 BpRefBase
client 端在查詢 SM 獲得所需的的 BpBinder 後, BpRefBase 負責管理當前獲得的 BpBinder 例項
6. 兩個介面類
6.1 BpINTERFACE
如果 client 想要使用 binder IPC 來通訊,那麼首先會從 SM 出查詢並獲得 server 端 service 的 BpBinder ,在 client端,這個物件被認為是 server 端的遠端代理。為了能夠使 client 能夠想本地呼叫一樣呼叫一個遠端 server , server 端需要向 client 提供一個介面, client 在在這個介面的基礎上建立一個 BpINTERFACE ,使用這個物件, client 的應用能夠想本地呼叫一樣直接呼叫 server 端的方法。而不用去關心具體的 binder
IPC 實現。
下面看一下 BpINTERFACE 的原型:
class BpINTERFACE : public BpInterface<IINTERFACE>
順著繼承關係再往上看
template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
BpINTERFACE 分別繼承自 INTERFACE ,和 BpRefBase ;
● BpINTERFACE 既實現了 service 中各方法的本地操作,將每個方法的引數以 Parcel 的形式傳送給 BD 。
例如 BpServiceManager 的
virtual status_t addService(const String16& name, const sp<IBinder>& service)
{
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
● 同時又將 BpBinder 作為了自己的成員來管理,將 BpBinder 儲存在 mRemote 中, BpServiceManager 通過呼叫BpRefBase 的 remote() 來獲得 BpBinder 指標。
6.2 BnINTERFACE
在定義 android native 端的 service 時,每個 service 均繼承自 BnINTERFACE(INTERFACE 為 service
name) 。BnINTERFACE 型別定義了一個 onTransact 函式,這個函式負責解包收到的 Parcel 並執行 client 端的請求的方法。
順著 BnINTERFACE 的繼承關係再往上看,
class BnINTERFACE: public BnInterface<IINTERFACE>
IINTERFACE 為 client 端的代理介面 BpINTERFACE 和 server 端的 BnINTERFACE 的共同介面類,這個共同介面類的目的就是保證 service 方法在 C-S 兩端的一致性。
再往上看
class BnInterface : public INTERFACE, public BBinder
同時我們發現了 BBinder 型別,這個型別又是幹什麼用的呢?既然每個 service 均可視為一個 binder ,那麼真正的server 端的 binder 的操作及狀態的維護就是通過繼承自 BBinder 來實現的。可見 BBinder 是 service 作為 binder 的本質所在。
那麼 BBinder 與 BpBinder 的區別又是什麼呢?
其實它們的區別很簡單, BpBinder 是 client 端建立的用於訊息傳送的代理,而 BBinder 是 server 端用於接收訊息的通道。檢視各自的程式碼就會發現,雖然兩個型別均有 transact 的方法,但是兩者的作用不同, BpBinder 的 transact 方法是向IPCThreadState 例項傳送訊息,通知其有訊息要傳送給 BD ;而 BBinder 則是當 IPCThreadState 例項收到 BD 訊息時,通過 BBinder 的 transact 的方法將其傳遞給它的子類 BnSERVICE 的 onTransact 函式執行 server 端的操作。
7. Parcel
Parcel 是 binder IPC 中的最基本的通訊單元,它儲存 C-S 間函式呼叫的引數 . 但是 Parcel 只能儲存基本的資料型別,如果是複雜的資料型別的話,在儲存時,需要將其拆分為基本的資料型別來儲存。
簡單的 Parcel 讀寫不再介紹,下面著重介紹一下 2 個函式
7.1 writeStrongBinder
當 client 需要將一個 binder 向 server 傳送時,可以呼叫此函式。例如
virtual status_t addService(const String16& name, const sp<IBinder>& service)
{
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
看一下 writeStrongBinder 的實體
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
return flatten_binder(ProcessState::self(), val, this);
}
接著往裡看 flatten_binder
status_t flatten_binder(const sp<ProcessState>& proc,
const sp<IBinder>& binder, Parcel* out)
{
flat_binder_object obj;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
if (binder != NULL) {
IBinder *local = binder->localBinder();
if (!local) {
BpBinder *proxy = binder->remoteBinder();
if (proxy == NULL) {
LOGE("null proxy");
}
const int32_t handle = proxy ? proxy->handle() : 0;
obj.type = BINDER_TYPE_HANDLE;
obj.handle = handle;
obj.cookie = NULL;
} else {
obj.type = BINDER_TYPE_BINDER;
obj.binder = local->getWeakRefs();
obj.cookie = local;
}
} else {
obj.type = BINDER_TYPE_BINDER;
obj.binder = NULL;
obj.cookie = NULL;
}
return finish_flatten_binder(binder, obj, out);
}
還是拿 addService 為例,它的引數為一個 BnINTERFACE 型別指標, BnINTERFACE 又繼承自 BBinder ,
BBinder* BBinder::localBinder()
{
return this;
}
所以寫入到 Parcel 的 binder 型別為 BINDER_TYPE_BINDER ,同時你在閱讀 SM 的程式碼時會發現如果 SM 收到的service 的 binder 型別不為 BINDER_TYPE_HANDLE 時, SM 將不會將此 service 新增到 svclist ,但是很顯然每個service 的新增都是成功的, addService 在開始傳遞的 binder 型別為 BINDER_TYPE_BINDER , SM 收到的 binder 型別為 BINDER_TYPE_HANDLE ,那麼這個過程當中究竟發生了什麼?
為了搞明白這個問題,花費我很多的事件,最終發現了問題的所在,原來在 BD 中做了如下操作(drivers/staging/android/Binder.c) :
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply)
{
..........................................
if (fp->type == BINDER_TYPE_BINDER)
fp->type = BINDER_TYPE_HANDLE;
else
fp->type = BINDER_TYPE_WEAK_HANDLE;
fp->handle = ref->desc;
..........................................
}
閱讀完 addService 的程式碼,你會發現 SM 只是儲存了 service binder 的 handle 和 service 的 name ,那麼當 client 需要和某個 service 通訊了,如何獲得 service 的 binder 呢?看下一個函式
7.2 readStrongBinder
當 server 端收到 client 的呼叫請求之後,如果需要返回一個 binder 時,可以向 BD 傳送這個 binder ,當IPCThreadState 例項收到這個返回的 Parcel 時, client 可以通過這個函式將這個被 server 返回的 binder 讀出。
sp<IBinder> Parcel::readStrongBinder() const
{
sp<IBinder> val;
unflatten_binder(ProcessState::self(), *this, &val);
return val;
}
往裡檢視 unflatten_binder
status_t unflatten_binder(const sp<ProcessState>& proc,
const Parcel& in, sp<IBinder>* out)
{
const flat_binder_object* flat = in.readObject(false);
if (flat) {
switch (flat->type) {
case BINDER_TYPE_BINDER:
*out = static_cast<IBinder*>(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
case BINDER_TYPE_HANDLE:
*out = proc->getStrongProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast<BpBinder*>(out->get()), *flat, in);
}
}
return BAD_TYPE;
}
發現如果 server 返回的 binder 型別為 BINDER_TYPE_BINDER 的話,也就是返回一個 binder 引用的話,直接獲取這個binder ;如果 server 返回的 binder 型別為 BINDER_TYPE_HANDLE 時,也就是 server 返回的僅僅是 binder 的 handle,那麼需要重新建立一個 BpBinder 返回給 client 。
有上面的程式碼可以看出, SM 儲存的 service 的 binder 僅僅是一個 handle ,而 client 則是通過向 SM 獲得這個 handle,從而重新構建代理 binder 與 server 通訊。
這裡順帶提一下一種特殊的情況, binder 通訊的雙方即可作為 client ,也可以作為 server. 也就是說此時的 binder 通訊是一個半雙工的通訊。那麼在這種情況下,操作的過程會比單工的情況複雜,但是基本的原理是一樣的,有興趣可以分析一下 MediaPlayer 和 MediaPlayerService 的例子。
8. 經典橋段分析
main_ mediaserver.cpp
int main(int argc, char** argv)
{
// 建立程式 mediaserver 的 ProcessState 例項
sp<ProcessState> proc(ProcessState::self());
// 獲得 SM 的 BpServiceManager
sp<IServiceManager> sm = defaultServiceManager();
LOGI("ServiceManager: %p", sm.get());
// 新增 mediaserver 中支援的 service 。
AudioFlinger::instantiate();
MediaPlayerService::instantiate();
CameraService::instantiate();
AudioPolicyService::instantiate();
// 啟動 ProcessState 的 pool thread
ProcessState::self()->startThreadPool();
// 這一步有重複之嫌,加不加無關緊要。
IPCThreadState::self()->joinThreadPool();
}
9. Java 層的 binder 機制
瞭解了 native 通訊機制後,再去分析 JAVA 層的 binder 機制,就會很好理解了。它只是對 native 的 binder 做了一個封裝。這一部分基本上沒有太複雜的過程,這裡不再贅述了。
相關文章
- android binder ipcAndroid
- Android的IPC機制BinderAndroid
- Android IPC機制(三):淺談Binder的使用Android
- Android的IPC機制(三)——Binder連線池Android
- Android IPC 機制分析Android
- android-IPC/Binder/D-BUS(Binder/Messager/AIDL)程式間通訊(訊息機制)AndroidAI
- Binder通訊機制與IPC通訊.md
- Android程式間通訊(IPC)機制Binder簡要介紹和學習計劃Android
- Binder學習(四)利用AIDL、Messenger實現IPCAIMessenger
- Android Binder之旅Android
- 【Android原始碼】Binder機制和AIDL分析Android原始碼AI
- 真香!為什麼Android要採用Binder作為IPC機制?全套教學資料Android
- Binder Java層分析Java
- Android-Binder(一)Android
- Binder機制分析(1)——Binder結構簡介
- Binder基礎業務分析
- Binder池淺談分析
- Android IPC 之AIDLAndroidAI
- Binder學習(三)通過AIDL分析Binder通訊流程AI
- Android Binder機制淺析Android
- Android中的IPC機制Android
- android IPC及原理簡介Android
- Android的IPC機制(四)—— Messenger的使用及原始碼分析AndroidMessenger原始碼
- 藉助 AIDL 理解 Android Binder 機制——Binder 來龍去脈AIAndroid
- electron-ipc通訊效能分析
- Android進階(六)Binder機制Android
- Android多程式之Binder的使用Android
- android Binder機制深入淺出Android
- Android aidl Binder框架淺析AndroidAI框架
- Android為什麼選擇binderAndroid
- Android Binder機制文章轉載Android
- Android 原始碼分析之旅2 1 IPC以及Service的啟動過程Android原始碼
- Binder機制分析(2)——從MediaService中看Binder的實現和使用(1)
- Binder機制分析(2)——從MediaService中看Binder的實現和使用(2)
- Binder Java層的實現原理分析Java
- 藉助 AIDL 理解 Android Binder 機制——AIDL 的使用和原理分析AIAndroid
- Android Binder跨程式與非跨程式的傳輸異同原始碼分析Android原始碼
- 圖解Android中的binder機制圖解Android