天天看點

binder 執行個體分析

android的binder機制提供一種程序間通信的方法,使不同一個程序可以以類似遠端過程調用的形式調用另一個程序所提供的功能。

android的代碼中,與c/c++的binder包括一些類型和接口的定義和實作,相關的代碼在下面這幾個檔案中:

frameworks/base/include/utils/iinterface.h

frameworks/base/include/utils/binder.h

frameworks/base/include/utils/bpbinder.h

frameworks/base/include/utils/ibinder

frameworks/base/include/utils/parcel.h

frameworks/base/include/utils/ipcthreadstate.h

frameworks/base/include/utils/processstate.h

frameworks/base/libs/utils/binder.cpp

frameworks/base/libs/utils/bpbinder.cpp

frameworks/base/libs/utils/iinterface.cpp

frameworks/base/libs/utils/ipcthreadstate.cpp

frameworks/base/libs/utils/parcel.cpp

frameworks/base/libs/utils/processstate.cpp

為了了解這些類、接口之間的關系以及binder的實作機制,最好是結合一個例子來進行研究。我選擇的例子是android自帶的媒體播放器的實作。其媒體播放器的相關代碼在下面這些目錄中:

frameworks/base/include/media

frameworks/base/media

使用startuml的反向工程功能分析上面這些代碼,并進行了一定的整理之後,得到下面這幅類圖(點選可檢視原尺寸圖檔)。

binder 執行個體分析

android的媒體播放功能分成兩部分,一部分是媒體播放應用,一部分是媒體播放服務(mediaserver,在系統啟動時由init所啟動,具可參考init.rc檔案)。這兩部分分别跑在不同的程序中。媒體播放應用包括java程式和部分c++代碼,媒體播放服務是c++代碼,并且需要調用外部子產品opencore來實作真正的媒體播放。媒體播放應用和媒體播放服務之間需要通過binder機制來進行互相調用,這些調用包括:

(1)媒體播放應用向媒體播放服務發控制指令

(2)媒體播放服務向媒體播放應用發事件通知(notify)

媒體播放服務對外提供多個接口,在上面得圖中包括其中的2個接口:imediaservice和imediaplayer,imediaplayer用于建立和管理播放執行個體,而imediaplayer接口則是播放接口,用于實作指定媒體檔案的播放以及播放過程的控制。

上面的圖中還有媒體播放應用向媒體播放服務提供的1個接口:imediaplayerclient,用于接收notify。

這些接口因為需要跨程序調用,是以需要用到binder機制。每個接口包括兩部分實作,一部分是接口功能的真正實作(bninterface),這部分運作在接口提供程序中;另一部分是接口的proxy(bpinterface),這部分運作在調用接口的程序中。binder的作用就是讓這兩部分之間建立聯系。下圖是整個播放器的一個概要說明。

binder 執行個體分析

媒體播放器比較複雜一些,總共實作了3個接口,不過要了解binder的機制,隻需要研究其中一個接口就足夠了。在這裡選擇imediaplayerservice接口來看一下。

imediaplayerservice接口包括六個功能函數:create(url)、create(fd)、decode(url)、 decode(fd)、createmediarecord()、createmetadataretriever()。在這裡不介紹這些函數是做什麼的,我們隻關注如何通過binder還提供這些函數接口。

(二) 接口定義

(1) 定義接口類

首先定義imediaplayerservice類,這是一個接口類(c++的術語應該叫純虛類)。該接口類定義在檔案frameworks/base/include/media/imediaplayerservice.h。代碼如下:

class imediaplayerservice: public iinterface

{

public:

declare_meta_interface(mediaplayerservice);

virtual sp createmediarecorder(pid_t pid) = 0;

virtual sp createmetadataretriever(pid_t pid) = 0;

virtual sp create(pid_t pid, const sp& client, const char* url) = 0;

virtual sp create(pid_t pid, const sp& client, int fd, int64_t offset, int64_t length) = 0;

virtual sp decode(const char* url, uint32_t *psamplerate, int* pnumchannels, int* pformat) = 0;

virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *psamplerate, int* pnumchannels, int* pformat) = 0;

};

可以看到,在這個接口類中定義了imediaplayerservice需要提供的6個函數接口,因為是接口類,是以定義為純虛函數。需要注意這個接口類的名稱有嚴格要求,必須是以大寫字母i開始。

重點關注在這些函數前面的一個宏定義: declare_meta_interface(mediaplayerservice)。這個宏定義必須要有,其中封裝了實作binder所需要的一些類成員變量和成員函數通過這些成員函數可以為一個binder實作建立proxy。這個宏定義在問價frameworks/base/include /utils/iinterface.h裡,在後面還會講到。這個宏定義的參數必須是接口類的名稱去除字母i後剩下的部分。

另外說明一下,可以看到接口類中所定義的函數的傳回值都是sp的形式,看起來有點怪異。sp是android中定義的一個模闆類,用于實作智能指針功能。sp就是imediaplayer的智能指針,可以簡單地把它看成是标準c++中的指針定義即 imediaplayer* 即可。

(2) 定義和實作binder類

binder類包括兩個,一個是接口實作類,一個接口代理類。接口代理類繼承自bpinterface,接口實作類繼承自bninterface。這兩個基類都是模闆類,封裝了binder的程序間通信機制,這樣使用者無需關注底層通信實作細節。

對于imediaplayerservice接口,其binder接口實作類為bnmediaplayerservice,接口代理類為 bpmediaplayerservice。需注意這兩個類的名稱有嚴格要求,必須以bn和bp開頭,并且後面的部分必須是前面所定義的接口類的名稱去除字母''''i’。比如前面所定義的接口類為imediaplayerservice,去除字母i後是mediaplayerservice,是以兩個 binder類的名稱分别是bnmediaplayerservice和bpmediaplayerservice。為什麼有這樣的要求?原因就在前面提到的宏定義declare_meta_interface()和另一個宏定義implement_meta_interface()裡面。有興趣的話可以去看一下,這兩個宏定義都在檔案frameworks/base/include/utils/iinterface.h裡。

bpmediaplayerservice是一個最終實作類。定義并且實作在在檔案frameworks/base/media/libmidia /imediaplayerservice.cpp中。在看bpmediaplayerservice的代碼之前,先看一下在 imediaplayerservice.cpp檔案的開始部分的一個枚舉定義:

enum {

create_url = ibinder::first_call_transaction,

create_fd,

decode_url,

decode_fd,

create_media_recorder,

create_metadata_retriever,

這些6個枚舉定義對應于imediaplayerservice接口所提供的6個功能函數,可以稱為這些功能函數的功能代碼,用于在程序之間進行rpc是辨別需要調用哪個函數。如果不想定義這些枚舉值,在後面需要用到這些值的地方直接寫上1,2,3,4,5,6也是可以的,不過……一個合适的程式員會這麼幹嗎?

下面看一下bpmediaplayerservice的代碼。

(3) bpmediaplayerservice代碼分析

class bpmediaplayerservice: public bpinterface

bpmediaplayerservice(const sp& impl)

: bpinterface(impl)

}

virtual sp createmetadataretriever(pid_t pid)

parcel data, reply;

data.writeinterfacetoken(imediaplayerservice::getinterfacedescriptor());

data.writeint32(pid);

remote()->transact(create_metadata_retriever, data, &reply);

return interface_cast(reply.readstrongbinder());

virtual sp create(pid_t pid, const sp& client, const char* url)

data.writestrongbinder(client->asbinder());

data.writecstring(url);

remote()->transact(create_url, data, &reply);

virtual sp createmediarecorder(pid_t pid)

remote()->transact(create_media_recorder, data, &reply);

virtual sp create(pid_t pid, const sp& client, int fd, int64_t offset, int64_t length)

data.writefiledescriptor(fd);

data.writeint64(offset);

data.writeint64(length);

remote()->transact(create_fd, data, &reply);

virtual sp decode(const char* url, uint32_t *psamplerate, int* pnumchannels, int* pformat)

remote()->transact(decode_url, data, &reply);

*psamplerate = uint32_t(reply.readint32());

*pnumchannels = reply.readint32();

*pformat = reply.readint32();

virtual sp decode(int fd, int64_t offset, int64_t length, uint32_t *psamplerate, int* pnumchannels, int* pformat)

remote()->transact(decode_fd, data, &reply);

首先可以看到,這個類繼承自模闆類bpinterface,指定類型為接口類imediaplayerservice。bpinterface模闆類定義在檔案iinterface.h。看一下bpinterface的定義就可以發現,bpmediaplayerservice這樣定義了以後,事實上間接繼承了imediaplayerservice,進而可以提供imediaplayerservice接口所定義的接口函數。 bpmediaplayerservice需要實作這些接口函數。在一個簡單的構造函數之後,就是這些接口函數的實作。可以看到,所有的接口函數的實作方法都是一緻的,都是通過binder所提供的機制将參數仍給binder的實作類,并擷取傳回值。這也就是這個類之是以成為代理類的原因。下面具體看一下一個接口函數。這裡選的是函數create(url)。

這個接口函數的參數指定了一個url,函數将為這個url建立一個播放器執行個體用于播放該url。

函數首先定義了兩個局部變量data和reply,變量的類型都是parcel。parcel是一個專為binder通信的資料傳送而定義的類,該類提供了對多種類型的資料的封裝功能,同時提供多個資料讀取和寫入函數,用于多種類型的資料的寫入和讀取,支援的資料類型既包括簡單資料類型,也包括對象。這裡定義的變量data是用于封裝create()函數調用所需要的輸入參數,而reply則是用于封裝調用的傳回資料(包括輸出參數的值和函數傳回值)。

函數首先向data中寫入各種資料。第一個寫入的是接口的一個描述字元串,binder的實作類中會用這個字元串來對接口做驗證,防止調用錯誤。這個字元串也可以不寫,如果不寫,在binder實作類中相應的也就不要做驗證了。跟在描述字元串後面寫入的是該接口函數所需要的各種的輸入參數。需要說明的是,pacel提供一種先入先出的資料存儲方式,即資料的寫入順序和讀取順序必須嚴格一緻,否則将會出錯。

完成資料寫入後,函數調用remote()->transact()用于完成binder通信。transact()函數的第一個參數就是前面提到過的功能代碼。transact()的功能是将data中的資料傳給binder的實作類,函數調用結束後,reply中将包含傳回資料。首先來看看 remote()成員函數。前面講到過bpmediaplayerservice通過繼承bpinterface模闆類間接繼承了 imediaplayerservice接口類,其實bpinterface類是一個有兩個父類的多重繼承子類,另一個父類是 bprefbase(frameworks/base/include/utils/binder.h)。remote()就是繼承自bprefbase 類的一個成員函數,該函數傳回bprefbase類中定義的一個私有屬性mremote。mremote是對ibinder接口類的子類bpbinder 的一個對象的引用(參考前面的類關系圖)。transact()函數在ibinder接口類中定義(frameworks/base/include /utils/binder.h),并在bpbinder類中實作(frameworks/base/include/utils /bpbinder.h、frameworks/base/libs/utils/bpbinder.cpp)。在transact()函數中将調用 ipcthreadstate類的transact()函數,并進而通過lniux核心中的android共享記憶體驅動來實作程序間通信。不過這些細節這裡就不多說了。在這裡bpbinder類對象是一個關鍵,是實作binder代理的核心之一。bpbinder類可以看成是一個通信handle(類似于網絡程式設計中的socket),用于實作程序間通信。接下來需要研究的是這個bpbinder類對象(即mremote成員變量的值)是從哪裡來的。

回過頭來bpmediaplayerservice的構造函數(看前面的代碼)。該構造函數的參數是一個ibinder對象的引用。mremote的值就是在這裡傳進來的這個對象。那麼這個對象又是怎麼來的呢?要搞清楚這一點就需要找到建立bpmediaplayerservice類的執行個體的代碼,這個代碼就就跟在該類的定義代碼的下面。繼續看imediaplayerservice.cpp檔案,在bpmediaplayerservice類定義的後面,是下面這樣一行代碼:

implement_meta_interface(mediaplayerservice, "android.hardware.imediaplayerservice");

這行代碼調用了一個宏定義implement_meta_interface()。這個宏定義與前面提到過的 declare_meta_interface()相呼應。看名字就知道,implement_meta_interface()宏是對 declare_meta_interface()所定義的成員函數的具體實作。這個宏的第一個參數與declare_meta_interface() 的參數需完全一樣,第二參數是接口的描述字元串(這個字元串前面也已經講到過了)。描述字元串不重要,重要的是宏裡面定義的一個靜态成員函數 asinterface()。bpmediaplayerservice的類執行個體是在imediaplayerservice的靜态成員函數 asinterface()中建立的,在iinterface.h中定義了一個内聯函數interface_cast(),對這個成員函數進行了封裝。通過看代碼容易知道,bpmediaplayerservice的構造函數的參數是通過interface_cast()的參數傳進來的。

好,下面就該看看這個interface_cast()是在哪裡調用的,它的參數到底是什麼。找到frameworks/base/media /libmedia/mediaplayer.cpp檔案,其中的mediaplayer::getmediaplayerservice()的實作代碼:

const sp& mediaplayer::getmediaplayerservice()

mutex::autolock _l(sservicelock);

if (smediaplayerservice.get() == 0) {

sp sm = defaultservicemanager();

sp binder;

do {

binder = sm->getservice(string16("media.player"));

if (binder != 0)

break;

logw("mediaplayerservice not published, waiting...");

usleep(500000); // 0.5 s

} while(true);

if (sdeathnotifier == null) {

sdeathnotifier = new deathnotifier();

binder->linktodeath(sdeathnotifier);

smediaplayerservice = interface_cast(binder);

loge_if(smediaplayerservice==0, "no mediaplayerservice!?");

return smediaplayerservice;

看一下上面這段代碼中的紅色字型部分。結合前面的分析,可知bpbinder類的對象執行個體是從android的服務管理器的getservice()函數中擷取,進一步追進去,會發現下面這樣一段代碼:

data.writeinterfacetoken(iservicemanager::getinterfacedescriptor());

data.writestring16(name);

remote()->transact(check_service_transaction, data, &reply);

return reply.readstrongbinder();

android的服務管理器是一個單獨的程序,也向外提供接口。這段代碼的含義,是通過android的服務管理器的接口代理,請求調用服務管理器的 checkservice()接口函數,查找指定的服務(上面就是查找media.player服務),查找成功後傳回一個bpbinder類的對象執行個體,用于供imediaplayerservice代理使用。這個對象bpbinder是在parcel::readstrongbinder()函數裡面建立的。那麼到底是怎麼建立出來的呢?在這裡沒有必要追到servicemanager的實作代碼裡去,畢竟我們隻是想知道bpbinder的對象是如何建立的,我們可以換一個例子來看。回到前面的bpmediaplayerservice::create()函數的實作,是不是很眼熟。沒錯,在那個函數裡也建立了一個bpbinder類對象,那個對象是是給imediaplayer接口代理使用的。雖然接口不同,但是建立原理是一樣的。我們繼續,下面該到binder的另一個類——實作類的代碼了。

(3) bnmediaplayerservice代碼分析

bnmediaplayerservice類的定義在檔案frameworks/base/include/media /imediaplayservice.h,實作則與bpmediaplayerservice一樣是在檔案frameworks/base/media /libmidia/imediaplayerservice.cpp中。類定義的代碼如下:

class bnmediaplayerservice: public bninterface

virtual status_t ontransact( uint32_t code,

const parcel& data,

parcel* reply,

uint32_t flags = 0);

這個類繼承自bninterface模闆類,限制類型為imediaplayerservice。看一下bninterface模闆類的定義(iinterface.h)就可以知道,bnmediaplayerservice間接繼承了imediaplayerservice接口類。不過 bninterface類并沒有實作imediaplayerservice所定義的6個接口函數,是以bninterface還是一個純虛類。這些接口需要在bnmediaplayerservice的子類中真正實作,這個子類就是mediaplayerservice(frameworks/base /media/libmidiaservice/mediaplayerservice.h,frameworks/base/media /libmidiaservice/mediaplayerservice.cpp)。在bnmediaplayerservice的成員函數 ontransact()中,需要調用這6個接口函數。bnmediaplayerservice中主要就是定義并實作了ontransact()函數。當在代理那邊調用了transact()函數後,這邊的ontransact()函數就會被調用。bnmediaplayerservice的實作代碼如下:

#define check_interface(interface, data, reply) /

do { if (!data.enforceinterface(interface::getinterfacedescriptor())) { /

logw("call incorrectly routed to " #interface); /

return permission_denied; /

} } while (0)

status_t bnmediaplayerservice::ontransact(

uint32_t code, const parcel& data, parcel* reply, uint32_t flags)

switch(code) {

case create_url: {

check_interface(imediaplayerservice, data, reply);

pid_t pid = data.readint32();

sp client = interface_cast(data.readstrongbinder());

const char* url = data.readcstring();

sp player = create(pid, client, url);

reply->writestrongbinder(player->asbinder());

return no_error;

} break;

case create_fd: {

int fd = dup(data.readfiledescriptor());

int64_t offset = data.readint64();

int64_t length = data.readint64();

sp player = create(pid, client, fd, offset, length);

case decode_url: {

uint32_t samplerate;

int numchannels;

int format;

sp player = decode(url, &samplerate, &numchannels, &format);

reply->writeint32(samplerate);

reply->writeint32(numchannels);

reply->writeint32(format);

case decode_fd: {

sp player = decode(fd, offset, length, &samplerate, &numchannels, &format);

case create_media_recorder: {

sp recorder = createmediarecorder(pid);

reply->writestrongbinder(recorder->asbinder());

case create_metadata_retriever: {

sp retriever = createmetadataretriever(pid);

reply->writestrongbinder(retriever->asbinder());

default:

return bbinder::ontransact(code, data, reply, flags);

首先是一個宏定義check_interface(),這個宏定義的作用是檢查接口的描述字元串,這個前面也提到過,不需細說。然後就是 ontrasact()函數的實作。這個函數的結構也很簡單,就是根據參數code的值分别執行不同的功能調用。code的取值就是前面提到過的接口功能代碼。函數的參數除了code,還包括parcel類的兩個對象data和reply,分别用于傳送輸入參數和傳回資料,與transact()函數的參數相對應。還有一個參數flag在這裡用不上,不讨論。對應我們前面所選擇的接口函數的例子create(url),看看這邊對應的實作:

首先是從data對象中依次取出各項輸入參數,然後調用接口函數create()(将在子類mediaplayerservice中實作),最後向reply中寫入傳回資料。這個函數傳回後,代理那邊的transact()也會跟着傳回。

那麼ontransact()函數是怎麼被調用的呢?通過檢視bninterface模闆類的定義可以看到,這個類也是一個多重繼承類,另一個父類是 bbinder(frameworks/base/include/utils/binder.h,frameworks/base/libs /utils/binder.cpp)。bbinder類繼承自ibinder,也實作了transact()函數,在這個函數中調用 ontransact()函數。而bbinder對象的transact()函數則是在ipcthreadstate類的 executecommand()成員函數中調用的。這已經涉及到較底層的實作,在這裡不再多說。

上面這部分代碼還與前面提到過的bpbinder對象的建立有關系。看其中的紅色字型部分,通過create()函數調用會建立一個 imediaplayer接口類的子類的對象,這個對象其實是mediaplayerservice::client類(可以看一下 mediaplayerservice的定義)的對象執行個體,而mediaplayerservice::client類是繼承自 bnmediaplayer類的,與bnmediaplayerservice類類似,bnmediaplayer其實也是一個binder實作類(是 bbinder的子類,進而也是ibinder的子類)。在上述代碼中,通過parcel的writestrongbinder()函數将這個對象寫入 reply,而在代理側,通過parcel的readstrongbinder()函數讀取則可以得到一個bpbinder的對象。至于類的具體建立過程已經封裝在parcel類的定義中,這裡就不再多說了。

(4) 接口功能的真正實作

到這裡兩個binder類就已經定義完了,下面就是imediaplayerservice接口函數的真正實作。前面已經說過這些函數在類 mediaplayerservice中實作。這個類繼承自bnmediaplayerservice,也間接地繼承了 imediaplayerservice接口類定義的6個功能函數,隻需要按照正常方式實作這6個功能函數即可,當然為了實作這6個函數就需要其它一大堆的東西,不過這些具體的實作方法已經與binder機制無關,不再多說。

在mediaplayerservice類中定義了一個靜态函數instantiate(),在這個函數中建立mediaplayerservice的對象執行個體,并将這個對象注冊到服務管理器中。這樣需要使用的時候就可以從服務管理器擷取imediaplayerservice的代理對象。這個 instantiate()是在mediaserver程式的main()函數中調用的。

void mediaplayerservice::instantiate() {

defaultservicemanager()->addservice(

string16("media.player"), new mediaplayerservice());

(三) 總結一下

說了這麼多,總結一下。下圖是binder機制的層次模型。

binder 執行個體分析

如果一個服務需要通過binder機制對外提供跨程序的接口,需要做下面這些事情。

(1) 第一步,需要為這個接口定義一個繼承自iinterface的接口類,假設叫做imyservice。

(2) 第二步,需要定義兩個binder類,其中一個是代理類bpmyservice,需繼承自bpinterface;另一個是實作類bnmyservice,需繼承自bninterface。

(3) 第三步,定義bnmyservice的子類,這個子類可以是任何名字,比如就叫myservice,在其中真正實作接口所提供的各個函數。

(4) 第四步,建立myservice的執行個體,注冊到服務管理器(如imediaplayerservice),也可以在其它接口的函數中建立(如上面的imediaplayer)。

上一篇: binder
下一篇: binder 分析