Android Binder IPC分析

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 做瞭一個封裝。這一部分基本上沒有太復雜的過程,這裡不再贅述瞭
 
摘自 杜文濤的專欄

發佈留言