再一次聊Binder 二
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做了一個封裝。相關文章
- Binder面試系列之二面試
- 理解 Android Binder 機制(二):C++層AndroidC++
- 聊一聊二分查詢法
- Binder
- Binder總結篇1-Binder原理
- Binder總結篇2-Binder使用
- 聊一聊設計模式(二)-- 建立型設計模式設計模式
- 聊一聊 Spring 中的擴充套件機制(二) - NamespaceHandlerSpring套件namespace
- Android Binder之旅Android
- Binder機制
- c++ binderC++
- 聊一聊 軟體系統中的“熱力學第二定律”
- Android-Binder(一)Android
- Binder Java層分析Java
- 聊一聊解謎遊戲的設計(二):機制與關卡遊戲
- 藉助 AIDL 理解 Android Binder 機制——Binder 來龍去脈AIAndroid
- Binder通訊機制
- binder核心原理解析
- Binder機制之AIDLAI
- Binder驅動的使用
- Binder關鍵問題
- 再一次學習 MySQL 索引MySql索引
- 由淺入深 學習 Android Binder(三)- java binder深究(從java到native)AndroidJava
- 對AIDL和Binder的理解AI
- Binder 驅動詳解(下)
- Binder 驅動詳解(上)
- Binder + AMS + AIDL大雜燴AI
- Binder基礎業務分析
- 聊一聊 RestTemplateREST
- 三流面試聊技術,二流面試聊框架,一流面試…面試框架
- 聊一聊 TLS/SSLTLS
- Android Binder機制文章轉載Android
- Android進階(六)Binder機制Android
- Android多程式之Binder的使用Android
- 聊一聊Greenplum與PostgreSQLSQL
- 聊一聊模板方法模式模式
- 聊一聊 JVM 的 GCJVMGC
- 聊一聊測試流程