Binder關鍵問題

afxstarx發表於2018-08-27

如何利用關鍵問題,感性理解Binder

1.service_manager的啟動

用來管理服務,所有的系統服務都會註冊到這裡。

本質上是服務的引用BpBinder的管理者,可以將服務引用BpBinder分發給其他Client。

當然了,這種分發在核心中是傳遞binder_node到其他程式,並在該程式中建立新的binder_ref和 BpBinder

思考:

       service_manager是如何成為管理者的 
        {
              必須是公共入口
              
              提供了儲存服務
              
       }
       
       如何與管理者通訊 {
       
              /dev/binder休眠喚醒
              
              資料協議
              
      }
複製程式碼

2.BpBinder、BBinder、IPCThreadState、ProcessState之間的關係

程式複用的底層與/dev/Binder的互動邏輯,全部放到了IPCThreadState中,資料的分發是靠Parcel + binder_transaction_data -> binder_write_read協議來實現的,所以只要將資料打包好,自然能夠傳送到目的地。

ProcessState主要快取了BpBinder,實現放到IPCThreadState中也沒有什麼問題。

3.BpInterface、BnInterface、BpBinder、BBinder之間的關係

C++層的aidl而已,這個理解應用比較到位吧

Java層的

    IXXXService.Stub繼承自Binder對映了JNI中的JavaBBinder

    IXXXService.Proxy繼承自BinderProxy,從IPCProcessState傳過來的Parcel,最終解析成為BpBinder,轉化為了Java層的BinderProxy。
複製程式碼

4.如何劃分Binder架構層次

Java層:

             Binder.exeTransact
複製程式碼

C++層:

           (BnInterface)
            JavaBBinder.onTransact    (data)
            
            BBinder.transact  (data、BBinder -> Parcel)
            
            IPCThreadState::getAndExecuteCommand  (Parcel -> binder_transaction_data ->
            
            binder_write_read )
            
            IPCThreadState::talkWifiDriver + waitForResponse
複製程式碼

核心層:

            ioctl
            /dev/binder          (binder_write_read -> binder_transaction_data -> binder_write_read)
            binder_ioctl
            binder_write_read
            binder_write_thread
            binder_transaction
複製程式碼

5.一次有效的通訊是如何實現的

無論是Java層還是C++層的上層呼叫,都是利用BpBinder -> IPCThreadState來傳送資料,利用BBinder-> IPCThreadState來接收資料。一次有效的資料通訊涉及到幾個方面:

1.BBinder實體的註冊

2.BpBinder引用的獲取

3.利用上層資料(data + flat_binder_object + 協議) + 傳輸handle 進行傳輸

4.核心驅動資料轉發實現

6.如和註冊到實名binder的

要註冊實名binder(service_manager進行一次add,其他binder先得獲得引用,然後執行add操作) A.service_manager中執行add,先獲取其引用BpBinder(0),Java層構建了ServiceManager_Proxy->Binder_Proxy->BpBinder(0),可以直接通訊了。

1.一個服務在Java層註冊

利用aidl生成Stub和Proxy,實現Stub的介面,利用ServiceManager.addService(name, Stub)註冊到ServiceManager中.程式碼如下:

142    public void addService(String name, IBinder service, boolean allowIsolated)
143            throws RemoteException {
144        Parcel data = Parcel.obtain();
145        Parcel reply = Parcel.obtain();
146        data.writeInterfaceToken(IServiceManager.descriptor);
147        data.writeString(name);
148        data.writeStrongBinder(service);
149        data.writeInt(allowIsolated ? 1 : 0);
150        mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
151        reply.recycle();
152        data.recycle();
153    }
複製程式碼

Java層的Binder寫到Parcel中變成了ibinderForJavaObject(env, object),即轉換成了JavaBBinder,並與Stub構建了關係關係,即JavaBBinder (BBinder) <-> Stub (Binder)

2.一個服務在C++層註冊

利用BpInterface和BnInterface約定關係,實現BnXXXService,並實現其中的code與函式呼叫關係。BnXXXService是BBinder的子類,最後呼叫C++層的IServiceManager.defaultServiceManager.addService(name, BBinder)來註冊。

virtual status_t addService(const String16& name, const sp<IBinder>& service,
          bool allowIsolated)  {
       Parcel data, reply;
       data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
       data.writeString16(name);
       data.writeStrongBinder(service);
       data.writeInt32(allowIsolated ? 1 : 0);
       status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
       return err == NO_ERROR ? reply.readExceptionCode() : err;
}       
複製程式碼

這裡還需要記錄一點:Binder節點基本上是第一次註冊到實名Binder時,在通訊的兩個程式中,分別在核心中建立的binder_node。當然了,在傳送端是建立binder_node節點,而在接收端是拷貝binder_node節點。

7.如何拿到引用以及引用如何找到目標

要拿到引用,只要跟實名binder通訊一次(servcie_manager是一次、其他實名binder是兩次) 如果要從service_manager中拿到引用,首先在IPCThreadState中找到BpBinder(0),利用其構造getService的通訊handle + data + target binder -> Parcel -> binder_transaction_data -> binder_write_read,利用通訊協議將binder_transaction_data.handle傳遞到核心,核心根據handle去查詢binder_ref (兩處查詢,一個是binder_proc->refs_by_desc,一個是binder_context_mgr_node),進而查詢到目標binder_node。利用binder_node通訊的過程:喚醒目標binder執行緒、取資料、解析,於是到了service_manager的上層了,後續不詳述了。

1.Java層如何找到服務

ServiceManager.getService在Java層是帶有快取的,如果有快取那麼直接取,如果沒有那麼利用ServiceManagerProxy -> BinderProxy -> BpBinder(0)來獲取服務,看下程式碼吧。最後得到的是一個經JNI轉換的BinderProxy。native得到的是BpBinder,而Java很顯然不能使用,瞭解Binder層次的話,很自然能夠確定一定是Binder_Proxy。

118    public IBinder getService(String name) throws RemoteException {
119        Parcel data = Parcel.obtain();
120        Parcel reply = Parcel.obtain();
121        data.writeInterfaceToken(IServiceManager.descriptor);
122        data.writeString(name);
123        mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
124        IBinder binder = reply.readStrongBinder();
125        reply.recycle();
126        data.recycle();
127        return binder;
128    }
複製程式碼

2.在C++層找到服務

同樣利用BpInterface來找到服務,先構建了一個BpServiceManager (繼承自BpInterface和IServiceManager),然後通訊就好了。最後得到的是一個BpBinder

gDefaultServiceManager = interface_cast<IServiceManager>(ProcessState::self()->getContextObject(NULL));

146    virtual sp<IBinder> getService( const String16& name) const
147    {
148        Parcel data, reply;
149        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
150        data.writeString16(name);
151        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
152        return reply.readStrongBinder();
153    }
複製程式碼

8.Java層和C++層的對映關係

Binder構造時:

JavaBBinder 放到了JavaBBinderHolder 的mBinder域, JavaBBinderHolder放到了 Binder.mObject 域,而Binder引用放到了 JavaBBinder.mObject,因此Binder和JavaBBinder建立了Java到C++的雙向關聯引用關係。

BinderProxy構造時:

基本在C++裡面出發,將BpBinder放到BinderProxy.mObject,而例如ServiceManagerProxy將BinderProxy放到了ServiceManagerProxy.mRemote域中。

9.Android層服務的註冊和引用

androidxref.com/5.0.0_r2/xr…

androidxref.com/5.0.0_r2/xr…

androidxref.com/5.0.0_r2/xr…

androidxref.com/5.0.0_r2/xr…

androidxref.com/5.0.0_r2/xr…

androidxref.com/5.0.0_r2/xr…

androidxref.com/5.0.0_r2/xr…

首先註冊Service到SystemServer程式,然後在Context中靜態初始化Client,應用利用Context.getSystemServer即可找到IVibratorService.Proxy了。

相關文章