天天看點

Android系統程序間通訊之Binder機制(一)Android系統程序間通訊之Binder機制(一)

Android系統程序間通訊之Binder機制(一)

                                                                   ----理論篇

    首先我們知道Android是基于Linux核心的,而Linux核心繼承和相容了豐富的Unix系統程序間通訊(IPC:Internet Process Connection),其中包括:

        1、Pipe:管道

        2、Signal:信号

        3、Trace:跟蹤

    以上是傳統的通訊手段,并且隻能用與父程序與子程序之間的通訊,或者兄弟之間的通訊。

        4、Message:封包隊列

        5、Share Memory:共享記憶體

        6、Semaphore:信号量

    以上三種是在AT&T的Unix系統V中,又增加了三種稱為“SystemV IPC”的程序間通信機制。

        7、Socket:插口

     其中,Socket是BSD Unix對“System VIPC”機制進行了重要的擴充。

     想對以上幾種通訊方式詳細了解可參考《Linux核心源代碼情景分析》一書。

     而在Android中采用的是Binder機制實作程序間的通訊。

     Binder是一種程序間通信機制,它是一種類似于COM和CORBA分布式元件架構,通俗一點,其實是提供遠端過程調用(RPC)功能。使用Client-Server通信方式:一個程序作為Server提供諸如視訊/音頻解碼,視訊捕獲,位址本查詢,網絡連接配接等服務;一個或者多個程序作為Client向Server發起服務請求,獲得所需要的服務。

     在Android的Binder機制中,包含四個角色,分别是:Server、Client、ServiceManage、Binder驅動。關系如下圖:

Android系統程式間通訊之Binder機制(一)Android系統程式間通訊之Binder機制(一)

   其中:

    Binder驅動:運作于核心空間,Binder機制的核心元件,提供裝置檔案/dev/binder與使用者空間互動。

    Server、Client、ServiceManage:運作于使用者空間,Service Manager提供了輔助管理的功能,Client和Server在Binder驅動和Service Manager提供的基礎設施上,進行Client-Server之間的通信。

    Service Manager和Binder驅動已經在Android平台中實作好,開發者隻要按照規範實作自己的Client和Server元件就可以了,具體實作可見下篇:Android系統程序間通訊之Binder機制(二) - 實踐篇。

    下面就AndroidBinder所會涉及的情景具體分析。

Service Manage

    Service Manager是一個linux級的程序,實作對Service的管理。同樣的,任何service在被使用之前,均要向Service Manager注冊(addService函數實作);同時用戶端需要通路某個service時,應該首先向Service Manager查詢是否存在該服務,并擷取到這個Server(getService函數實作)。

    Service Manager的入口函數在service_manager.c中,下面是 Service Manager的代碼部分:

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.指定Service Manager對應的代理binder的handle為0,當client嘗試與Service Manager通信時,需要建立一個handle為0的代理binder,這裡的代理binder其實就是第一節中描述的那個代理接口;

       3.通知binder driver(BD)使Service Manager成為BD的contextmanager;

       4.維護一個死循環,在這個死循環中,不停地去讀核心中binder driver,檢視是否有可讀的内容;即是否有對service的操作要求,如果有,則調用svcmgr_handler回調來處理請求的操作。

       5. Service Manager維護了一個svclist清單來存儲service的資訊。

Android系統程式間通訊之Binder機制(一)Android系統程式間通訊之Binder機制(一)

 注意:

    1、 當service在向SM注冊時,該service就是一個client,而SM則作為了server。而某個程序需要與service通信時,此時這個程序為client,service才作為server。是以service不一定為server,有時它也是作為client存在的。

    2、 應用和service之間的通信會涉及到2次binder通信:

        a)      應用向SM查詢service是否存在,如果存在獲得該service的代理binder,此為一次binder通信;

        b)     應用通過代理binder調用service的方法,此為第二次binder通信。

Server

    首先,Server和Client的實作都是Service Manager實作的,對Server啟動而言:Server通過defaultServiceManager函數獲得Service Manager遠端接口,然後将自己的Service添加到Service Manager中去,接着把自己啟動起來,等待Client的請求。下面将通過分析源代碼了解Server的實作和啟動過程是怎麼樣的。

    假設我們需要定義一個Server:ExampleService。

    首先,我們需要定義一個IExampleService(繼承了IInterface類),然後定義BnExampleService(繼承了IExampleService和BBinder類)。其中BBinder類繼承了IBinder類,IInterface和IBinder類又同時繼承了RefBase類。

然而BnExampleService并不是直接接收到Client處發送過來的請求,而是使用了IPCThreadState接收Client處發送過來的請求,而IPCThreadState又借助了ProcessState類來與Binder驅動程式互動。IPCThreadState接收到了Client處的請求後,就會調用BBinder類的transact函數,并傳入相關參數,BBinder類的transact函數最終調用BnMediaPlayerService類的onTransact函數,于是,就開始真正地處理Client的請求了。(其中有關IPCThreadState和ProcessState的關系下面會有介紹)。以上便是Server的實作過程。

    那麼對ExampleService的啟動流程呢?

    首先:

sp<ProcessState> proc(ProcessState::self());
           

    這句代碼的作用是通過ProcessState::self()調用建立一個ProcessState執行個體。ProcessState::self()是ProcessState類的一個靜态成員變量,定義在frameworks/base/libs/binder/ProcessState.cpp檔案中。

    這個函數作用是傳回一個全局唯一的ProcessState執行個體gProcess。全局唯一執行個體變量gProcess定義在frameworks/base/libs/binder/Static.cpp檔案中。

    在ProcessState的構造函數中主要以下操作:

          1、  open_driver(frameworks/base/libs/binder/ProcessState.cpp)函數打開Binder裝置

檔案/dev/binder,并将打開裝置檔案描述符儲存在成員變量mDriverFD中:主要是通過open檔案操作函數來打開/dev/binder裝置檔案,然後再調用ioctl檔案控制函數來分别執行BINDER_VERSION和BINDER_SET_MAX_THREADS兩個指令來和Binder驅動程式進行互動,前者用于獲得目前Binder驅動程式的版本号,後者用于通知Binder驅動程式。(open在Binder驅動程式中的具體實作,打開/dev/binder裝置檔案後,Binder驅動程式就為ExampleService程序建立了一個struct binder_proc結構體執行個體來維護ExampleService程序上下文相關資訊)。

          2、  mmap來把裝置檔案/dev/binder映射到記憶體中。 mmap函數調用完成之後,Binder驅動程式就為目前程序預留了BINDER_VM_SIZE大小的記憶體空間了。

    至此,ProcessState全局唯一變量gProcess就建立完畢了。

    接下來:

       調用defaultServiceManager函數來獲得Service Manager的遠端接口,定義ExampleService::instantiate函數把ExampleService添加到Service Manger中去了,如下:

void ExampleService::instantiate() {
    defaultServiceManager()->addService(String16("exampleservice"));
}
           

    其中defaultServiceManager傳回的實際是一個BpServiceManger類執行個體,是以,我們具體了解一下BpServiceManger::addService的實作,這個函數實作在frameworks/base/libs/binder/IServiceManager.cpp檔案中。

    對addService的了解:BpServiceManger以Client的形式,将相應的一些資料通過Parcel類中各種Parcel.writexxx的方式寫入Parcel中,最後通過

status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
           

    将data傳遞,實作資訊互動傳遞(主要是Binder驅動程式之間,傳輸這個ExampleService)。實作ExampleService的啟動過程。

    最終實作的就是就是把ExampleService這個Binder實體的引用寫到一個struct svcinfo結構體中,主要是它的名稱和句柄值,然後插入到連結svclist的頭部去。Client來向Service Manager查詢服務接口時,隻要給定服務名稱,Service Manger就可以傳回相應的句柄值了,進而實作調用該Service。

    最後

ProcessState::self()->startThreadPool();  
IPCThreadState::self()->joinThreadPool();
           

在一個無窮循環中等待Client的請求。

Client

    同樣的,在這将簡單介紹一下Client的實作流程與擷取Server的流程,Client的實作流程原理跟Server如出一轍,擷取Server的方式也是通過ServiceManager實作的。

    假設我們定義一個Client:ExampleService。

    首先我們需要定義一個 IExampleService (繼承了IInterface類),然後定義 BpExampleService(繼承了IExampleService和Bpinder類)。其中Bpinder類繼承了IBinder類,IInterface和IBinder類又同時繼承了RefBase類。在BpExampleService調用remote()->transact實作資料傳遞。

    remote()是在BpExampleService的父類BpRefBase中實作的,傳回的就是一個BpBinder.實際上調用的就是BpBinder的transact,将寫入到Parcel中的資料傳遞出去并取得傳回值。

    對于Serve的啟動流程主要是調用addService 實作的,對于Client擷取Server的流程采用getService實作。

    聲明:

sp<IServiceManager> sm = defaultServiceManager();  
sp<IBinder> binder;
           

    因為可能存在相對應需要擷取的Server可能還沒有啟動起來,是以這裡如果發現取回來的binder接口為NULL,就睡眠0.5秒,然後再嘗試擷取,這是擷取Service接口的标準做法:

do {  
   binder = sm->getService(String16("exampleservice"));  
      if (binder != 0) {  
           break;  
         }  
      LOGW("service not published, waiting...");  
       usleep(500000); // 0.5 s  
        } while(true);
           

    對于BpServiceManager::getService的具體實作在這就不做詳細介紹,跟Server的addService如出一轍。

    接着,

sExampleService = interface_cast<IExampleService>(binder);
           

    這裡的interface_cast實際上最終調用了IExampleService::asInterface函數,建立一個BpExampleService對象供我們得到。有了這個BpExampleService這個遠端接口之後,Client就可以調用ExampleService的服務了。

    最後,在 BpExampleService 下定義相應的方法實作資訊傳遞 : 定義一個 Parcel data ,将資料寫入,然後調用 remote()->transact 函數将資料傳遞出去,在 Server 下的 onTransact 函數就能接收到相應的 read 出相應的資料。

IBinder, BBinder和BpBinder

    首先BBinder(也就是BnBinder)與BpBinder均為IBinder的子類,是以可以看出IBinder定義了binder IPC的通信協定,BBinder與BpBinder在這個協定架構内進行的收和發操作,建構了基本的binder IPC機制。

ProcessState, IPCThreadState

    ProcessState是以單例模式設計的。每個程序在使用binder機制通信時,均需要維護

一個ProcessState執行個體來描述目前程序在binder通信時的binder狀态。ProcessState有如下2個主要功能:

     1.建立一個thread,該線程負責與核心中的binder子產品進行通信,稱該線程為Pool thread;(Poolthread:在BinderIPC中,所有程序均會啟動一個thread來負責與BD來直接通信,也就是不停的讀寫BD,這個線程的實作主體是一個IPCThreadState對象. Poolthread的啟動方式:ProcessState::self()->startThreadPool();)

     2.為指定的handle建立一個BpBinder對象,并管理該程序中所有的BpBinder對象。

    IPCThreadState也是以單例模式設計的。由于每個程序隻維護了一個ProcessState執行個體,同時ProcessState隻啟動一個Pool thread,也就是說每一個程序隻會啟動一個Pool thread,是以每個程序則隻需要一個IPCThreadState即可。

    Pool thread的實際内容則為:IPCThreadState::self()->joinThreadPool();

    IPCThreadState有兩個重要的函數,talkWithDriver函數負責從BD讀寫資料,executeCommand函數負責解析并執行mIn中的資料。

    ProcessState&IPCThreadState&BpBinder&BinderDriver關系圖:

Android系統程式間通訊之Binder機制(一)Android系統程式間通訊之Binder機制(一)

Parcel

    Parcel是binderIPC中的最基本的通信單元,它存儲C-S間函數調用的參數.但是Parcel隻能存儲基本的資料類型,如果是複雜的資料類型的話,在存儲時,需要将其拆分為基本的資料類型來存儲。Parcel類下定義了相應的write和read方法寫入和讀取資料。

       注:寫入和讀取的順序需要一樣,否則會出錯。

具體的實作可參見下篇本人具體就項目采用的binder通訊機制的實作:Android系統程序間通訊之Binder機制(二) - 實踐篇。