Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 從java到C解析Binder機制

從java到C解析Binder機制

編輯:關於Android編程

Binder機制是一種C/S結構,主要包括三部分,分別為Client、Server、ServiceManager。ServiceManager是谷歌設計的,它是一段簡潔的C代碼(位置在native/cmds/servicemanager/service_manager.c),主要是方便集中管理系統中的各種服務,也方便Client和Server查詢系統中的服務信息。

我們首先來看一個跟ServiceManager很大關聯的類,它就是IServiceManager類:

\

該類包括四個方法,名字就不重復了,通過這些方法可以注冊服務、查詢服務信息等,另外它還有一個enum枚舉,enum包含與方法相對應的四個常量。查看源碼我們可知enum常量的值為1,2,3,4。

GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,

CHECK_SERVICE_TRANSACTION,

ADD_SERVICE_TRANSACTION,

LIST_SERVICES_TRANSACTION,

};

其中IBinder::FIRST_CALL_TRANSACTION的值為1,這裡的取值是很重要的,因為他們分別對應ServiceManager(service_manager.c)中的enum常量:

enum {

SVC_MGR_GET_SERVICE = 1,

SVC_MGR_CHECK_SERVICE,

SVC_MGR_ADD_SERVICE,

SVC_MGR_LIST_SERVICES,

};

該enum在native/cmds/servicemanager/binder.h位置,service_manager.c程序引用了binder.h。這樣C/S實現IServiceManager接口後,如果C/S傳遞GET_SERVICE_TRANSACTION參數,ServiceManager(service_manager.c)就能對應SVC_MGR_GET_SERVICE處理了,其他類似。

 

之所以開頭就講IServiceManager類,是因為我覺得從宏觀上對C/S和ServiceManager的關系有一個大概的印象很重要,如下圖所示:

\

 

如何看到這裡你在好奇使用的是什麼軟件畫圖的,我鄭重的推薦一下Gliffy畫圖軟件,而且它有chrome插件,相比viso等軟件更輕、更簡單。

在上圖IServiceManager和ServiceManager之間什麼都沒有畫,那他們是如何通信的呢?其實他們是通過一種叫Binder設備進行通信的,這也是今天的主題,如果不了解設備的概念,建議查看linux的相關知識。我們將Binder設備加入可得到下圖:

\

 

 

從上圖我們知道了IServiceManager和ServiceManager可以通過Binder通信,但是還沒有和C/S關聯,我們加上C/S後來看看效果圖,如下所示:

 

\

因為Client與binder通信在java層涉及的層次太多,我們主要分析Server與Binder的通信。

 

 

Server到BpServiceManager的過程

我們以MediaPlayerService為例,MediaPlayerService的Server端是main_mediaserver文件,android5.1.0的源碼位置在/frameworks/av/media/mediaserver/main_mediaserver.cpp,代碼如下:

\

 

main_mediaserver作為Server端,肯定會與mediaplayerservice關聯,defaultServiceManager方法可以獲得BpServiceManager對象,這樣通過該對象就能與binder設備通信。MediaPlayerService::instantiate()這句很重要,是main_mediaserver和mediaplayerservice關聯的橋梁,該方法的具體代碼如下:

voidMediaPlayerService::instantiate() {

defaultServiceManager()->addService(

String16("media.player"),new MediaPlayerService());

}

我們看出instantiate方法的作用是將MediaPlayerService注冊到ServiceManager中,這裡又出現了defaultServiceManager方法,說明注冊服務也是通過BpServiceManager來執行的。

回到main_mediaserver我們找不到defaultServiceManager方法,但是我們可以找到如下聲明:

#include

我們迫不及待想要去看看defaultServiceManager方法的具體實現了,位置在native/libs/binder/IServiceManager.cpp),代碼如下所示:

\

從上面代碼我們只能看出gDefaultServiceManager是IServiceManager類,而不能確定它就是BpServiceManager類。我們繼續跟蹤,首先關注ProcessState類,它是一個記錄進程狀態的類。之前的圖中我們在Server和BpServiceManager之間添加的就是ProcessState,不記得可以回頭看一下,之所以放在這麼重要的位置,那是因為Binder設備的操作都是跟進程關聯的,一個進程共享一個Binder設備。我們來看看ProcessState::self()->getContextObject(NULL)具體內容:

\

我們跟進getStrongProxyForHandle(0)(位置在native/libs/binder/ProcessState.cpp),內容如下:

\

 

由上面的代碼我們知道getStrongProxyForHandle(0)返回的是BpBinder,所以defaultServiceManager()方法可以簡化為

gDefaultServiceManager= interface_cast(new BpBinder(0))

 

interface_cast在IServiceManager類中沒有定義,它是在父類頭文件IInterface.h中定義的,它是一個模板方法,位置在native/include/binder/IInterface.h中,如下所示:

\

上述代碼中的INTERFACE模板類為IServiceManager,這是在defaultServiceManager()方法中傳入的,所以gDefaultServiceManager =IServiceManager::asInterface(new BpBinder(0))。這樣我們跟蹤IServiceManager的asInterface()方法,但是在IServiceManager中沒有找到該方法,我們繼續在父類IInterface.cpp中找,我們可以找到如下代碼:

\

我們進一步簡化gDefaultServiceManager= new BpServiceManager(new BpBinder(0)),其實BpServiceManager沒有什麼特別的意義,它都是通過BpBinder去通信的,它的意義是就在於保存BpBinder對象以供MediaPlayerService調用。BpServiceManager中的成員變量mRemote就是用來保存BpBinder對象的,mRemote變量定義在BpRefbase類中,繼承關系如下:

\

階段總結,我們可以知道defaultServiceManager()方法得到是BpServcieManager對象,並且BpServiceManager對象中的變量mRemote保存了BpBinder對象。

BpServiceManager到Binder設備的過程

回顧之前的main_mediasever.cpp類,我們知道MediaPlayerService服務是通過MediaPlayerService::instantiate()這句代碼將自己注冊到ServerManager,我們再回顧一下具體顯示的代碼:

voidMediaPlayerService::instantiate() {

defaultServiceManager()->addService(

String16("media.player"),new MediaPlayerService());

}

之前我們分析過defaultServiceManager方法得到的是BpServiceManager,我們就以MediaPlayerService的addService過程為例分析一下《BpServiceManager到Binder設備的過程》。

我們進入BpServiceManager的addService方法看看,位置在native/libs/binder/IServiceManager.cpp,實現代碼如下:

\

注意data.writeStrongBinder(service)這句代碼,service就是MediaPlayerService::instantiate方法中new的MediaPlayerService對象。感興趣話可以追蹤writeStrongBinder方法的實現,它的主要作用就是將MediaPlayerService對象保存在Parcel對象中並通過binder設備傳遞給ServerManager ,這樣以後就可以通過該對象與ServiceManager和Client通信了。

remote()方法就是獲得mRemote對象,調用BpBinder的transact方法就能與Binder設備通信了,我們進入transact方法看一下:

\

 

IPCThreadState是什麼呢?之前我們介紹過ProcessState類,我們知道ProcessState類是記錄進程的狀態,IPCThreadState跟ProcessState有很大的關系,從字面意思來講,IPCThreadState就是IPC有關線程的狀態,也就是一個進程可能同時存在好多線程與外界的進程通過IPC通信,用圖描述如下:

\

 

我們進入IPCThreadState(位置在native/libs/binder/IPCThreadState.cpp)的transact方法中看看:

\

代碼中的writeTransactionData方法,主要是將數據寫入Pacel緩存中,方便進行比較等操作,但是真正的傳遞數據是在waitForResponse方法中,代碼如下:

\

 

注意talkWithDriver方法,我們再進入該方法中看看:

\

注意mProcess變量,由之前IPCThreadState和ProcessState關系圖我們知道該mProcess變量就是指向ProcessState對象,所以mProcess->mDriverFD指向的就是進程中唯一的Binder設備,這樣就可以將數據寫入Binder設備來傳遞數據。

ServiceManager處理Binder數據的過程

開頭我們就說了ServiceManager其實是一段C程序,那我們看看service_manager.c的代碼(位置在native/cmds/servicemanager/service_manager.c):

\

其中bs = binder_open(128*1024)就是打開binder設備,第一感覺就是128*1024的size是不是太小了,驅動相關的朋友有知道的請不吝賜教。我們關注後面的binder_loop方法,它就是負責操作binder設備,定義在native/cmds/servicemanager/binder.c

\

從代碼的內容可以看出該方法裡面包含了一個死循環,這樣一旦service_manager.c程序運行後,該程序就開始監聽binder設備的數據了。binder_write_read結構體是用來在binder設備傳遞數據的,如果需要了解更詳細的信息請參考其它資料。通過ioctl方法可以獲得binder設備中的數據,那有事怎麼解析的呢?我們看看binder_parse方法的代碼:

\

從代碼中可以看出,binder.c會根據ptr的指針所指類型進行不同的響應。

ServiceManager給予反饋後,Server的處理過程

現在我們已經分析了server->BpServiceManger->binder->ServiceManager的整個過程,並且我們知道在binder_parse會根據不同的請求作出響應,那server端是如何收到響應的呢?我們回過頭去看一下main_mediaserver.cpp的代碼:

\

以我們之前的分析經驗可以知道操作binder設備主要跟進程的ProcessState和IPCThreadState類有關,我們跟蹤IPCThreadState::self()->joinThreadPool()看看:

\

注意該方法中的talkWithDriver帶的參數是false,當Server端是注冊服務或初始化時傳遞的參數是false,我們現在分析的是Server端接收數據,所以我們分析getAndExecuteCommand方法,跟蹤進去看看:

\

 

這裡也看到talkWithDriver,而且默認傳遞的參數是true,不信的話可以去IPCThreadState.h頭文件看看,位置在native/include/binder/IPCThreadState.h。

有之前《BpServiceManager到Binder設備的過程》的最後一步talkWithDriver方法的分析,我們知道talkWithDriver方法會與binder設備打交道,通過ioctl方法傳遞數據,並將binder數據緩存到Parcel對象,那我們現在分析一下Server如何處理從Binder設備讀取的數據吧。首先從緩存Parcel中讀取數據:

cmd =mIn.readInt32()

此時的cmd的類型就是BR_TRANSACTION了,然後我們進入executeCommand方法看看,代碼太長我們截取主要部分:

\

關鍵就是spb((BBinder*)tr.cookie)這句代碼,為什麼ServiceManager從Binder設備中讀到是BBinder類型的數據呢?還記得我們之前在main_mediaserver是如何將MediaPlayerServcie注冊到ServiceManager的嗎?我們來回顧一下MediaPlayerService::instantiate()方法:

voidMediaPlayerService::instantiate() {

defaultServiceManager()->addService(

String16("media.player"),new MediaPlayerService());

}

我們傳遞進去的是MediaPlayerService對象,並通過後續BpServiceManager中的writeStrongBinder將MediaPlayerService打包成Parcel傳遞給ServiceManager的。但是即便這樣,MediaPlayerService跟BBinder又有什麼關系呢?我們來看一下MediaPlayerService的繼承關系:

\

很激動,我們發現MediaPlayerService繼承BBinder,那tr.cookie就是指向MediaPlayerService對象了。有了MediaPlayerService對象後,Server(main_mediaserver.cpp)就能調用MediaPlayerService的transact方法做出響應了,transact的具體實現我們就不分析了。

雖然我們知道了MediaPlayerService通過binder_transaction_data.cookie域傳遞,但是我們對Server和ServcieManager之間數據的傳遞沒有一個很直觀的印象,我相信你看完之後會有一個更深的理解。我們在這裡引用侯亮的圖片簡單說明一下:

\

我們簡單分析一下紅線表示的流程,當我們在Server端(即圖片中的發起端進程)需要向ServerManager(目標端進程)注冊服務時,在驅動層會創建實體樹的根binder_node,將Server端的MediaPlayerService對象保存到cookie域,只有被其他進程調用時才會創建引用樹的根binder_ref。當目標端進程需要用到Server端MediaPlayerService服務時,直接在驅動層訪問binder_node的cookie域就能拿到MediaPlayerServcie對象了,再通過transact方法傳遞數據。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved