Android Binder IPC分析

desaco發表於2016-02-24

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 做了一個封裝。這一部分基本上沒有太複雜的過程,這裡不再贅述了。

相關文章