2016年3月8日 星期二

[轉] Android Binder IPC分析

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

沒有留言:

張貼留言