https://blog.csdn.net/windskier/article/details/6317867
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 做了一個封裝。這一部分基本上沒有太複雜的過程,這裡不再贅述了。
---------------------
作者:windskier
来源:CSDN
原文:https://blog.csdn.net/windskier/article/details/6317867
版权声明:本文为博主原创文章,转载请附上博文链接!
沒有留言:
張貼留言