1. 程式人生 > >Service與Android系統設計(5)-- libbinder

Service與Android系統設計(5)-- libbinder


libbinder – Binder的Native實現

出於效能和程式碼統一性的角度考慮,Binder IPC並不Java和Native環境裡各實現一次,而只是分別在不同的執行環境裡提供使用的介面。使用Binder的Java程式碼,通過一些使用Binder的Java類之後,必須會走入到Native環境,將具體的分發的工作交給執行效率更高的Native程式碼。

最後這些介面都將被統一到一個統一的Binder互動環境,這一環境可以被稱為Binder環境,而Binder IPC通訊的過程,最終是在libbinder這樣一種庫檔案裡實現,最終會通過libbinder.so提供到Android系統裡。

 libbinder是由C++編寫的,這便Binder傳輸在實現上也具備面向物件的特點,這種能力不僅給Java環境裡的概念對映提供了方便,同時,也使在Native環境裡也以面向物件的方向來使用Binder進行多種環境的互相通訊提供了一種機制。於是,直接使用libbinder提供的程式設計介面,也可以編寫Native環境裡的System Service,由C++語言直接給Java語言提供服務端實現,這就得到了NativeService。

對於libbinder的使用,可以還是先從Java環境的Binder類開始分析。

Java環境如何訪問到libbinder

在對於Java環境的Remote Service分析時,我們可以看到Binder在Java環境裡的表現形式,除去只作為介面類的IInterface、IBinder,實際上在Android系統裡,只會使用一個Binder基類來託管一個遠端物件。從互動過程來看,使用某個Binder上託管的物件,都可以通過Binder之上搭建的IPC訊息“橋”進行互通過,在實現上,一個通過繼承的Binder的物件,分別將在傳送端產生Proxy部分,而在接收端生成Stub部分。這樣的Binder通過IInterface將物件暴露出來之後,傳送端就可以使用這一引用找到Proxy,通過Proxy物件的transact()方法傳送,而接收端對應的也使用同一IInterface,通過其上onTransact()回撥接收。這樣便構成了互動的能力。


但從Java環境裡,實際上,我們根本不知道是如何處理完成的,我們跟蹤所有的Binder相關的Java實現,也不看不出來。比如,我們在客戶端經常會使用這樣的方法來寫Proxy端方法:

    public IBinder getService(String name) throws RemoteException { 
       Parcel data = Parcel.obtain();
       Parcel reply = Parcel.obtain();
       data.writeInterfaceToken(IServiceManager.descriptor);
       data.writeString(name);
       mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
       IBinder binder = reply.readStrongBinder();
       reply.recycle();
       data.recycle();
       return binder;
}

這種Proxy端的大部分程式碼,都可以在Java原始碼裡跟蹤到其具體實現,但mRemote.transact()這行,則沒有跟蹤程式碼到具體是如何完成Binder通訊的,我們前面只是說這一行便是送資料從BinderIPC通道上傳送了出去,但並沒有解釋如何傳送出去的。

這就會涉及到Binder的“跨界”實現,Binder這種特殊跨程序物件,實際上會對映到三層環境裡,Kernel層,C/C++層,Java層。其中Kernel的Binder驅動是提供跨程序實現的基礎,能夠支援訪問Binder驅動只有C/C++層,Java層則必須要通過JNI訪問到C/C++層。Java語言實現裡並不會支援任何對於底層作業系統功能,因為這樣勢必會破壞Java在各平臺之上直接執行能力,但Java語言又將訪問幾乎所有的作業系統功能,這些都得通過JNI整合到Java環境的。在JNI程式設計這種限制之下,如果Binder物件在Java環境裡保持一份、C++環境一份,則將加大開發與維護的開銷。於是在Android世界裡,JNI層一般都很薄(甚至大部分還很難理解),儘可能將程式碼集中在C++層實現,這樣保持了實現上的簡潔,同時還收穫了執行上的更高的效率。

我們的Binder.java實現了IBinder介面類,於是它擁有如下的結構:

public class Binder implements IBinder {
    public static final native int getCallingPid();
    public static final native int getCallingUid();
    public static final int getOrigCallingUid();
    private static final native int getOrigCallingUidNative();
    public static final int getOrigCallingUser();
   
    public static final native long clearCallingIdentity();
    public static final native void restoreCallingIdentity(long token);
    public static final native void setThreadStrictModePolicy(int policyMask);
    public static final native int getThreadStrictModePolicy();
    public static final native void flushPendingCommands();
    public static final native void joinThreadPool();
   
    public Binder() {
        init();
        ...
    }
   
    public void attachInterface(IInterface owner, Stringdescriptor) ;
    public String getInterfaceDescriptor();
    public boolean pingBinder() ;
    public boolean isBinderAlive();
    public IInterface queryLocalInterface(Stringdescriptor);
    protected boolean onTransact(int code, Parcel data, Parcel reply,
                                 int flags) throws RemoteException;
    public void dump(FileDescriptor fd, String[] args);
 
    protected void dump(FileDescriptor fd, PrintWriter fout,String[] args);
    public final boolean transact(int code, Parcel data, Parcel reply,
                                  int flags) throws RemoteException;
 
    public void linkToDeath(DeathRecipient recipient, int flags) ;
    public boolean unlinkToDeath(DeathRecipient recipient, int flags);
   
    protected void finalize() throws Throwable {
            destroy();
    }
   
    private native final void init();
    private native final void destroy();
 
    private boolean execTransact(int code, int dataObj, int replyObj,
            int flags) {
        res = onTransact(code, data, reply,flags);
        return res;
    }
}

作為一個繼承至IBinder介面的類,Binder這個類勢必要實現所有的介面方法。但比較特殊的是Binder類的構造方法Binder()會呼叫到一個init()方法,析構方法finalize()會呼叫到一個destroy()方法,這兩個方法並不是由Java實現,而是被標識為native的JNI實現的方法。另外需要注意到的是,Binder類本身實現了transact()與onTransact()收發兩端的程式碼,但只是程序類的直接互動,並不會程序間的Binder互動。

另外,我們在同一Binder.java類裡,還可以找到另一個BinderProxy類的定義,在這一類裡會使用一個native標識的transact()方法。雖然在原始碼裡找不到任何使用這一類的地方,但從命名方式上來看,這一類應該會是被建立的預設的Proxy端。BinderProxy.java的定義如下:

final class BinderProxy implements IBinder {
    public native boolean pingBinder();
    public native boolean isBinderAlive();
    public IInterface queryLocalInterface(Stringdescriptor);
    public native String getInterfaceDescriptor() throws RemoteException;
    public native boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;
    public native void linkToDeath(DeathRecipient recipient,int flags)
            throws RemoteException;
    public native boolean unlinkToDeath(DeathRecipient recipient,int flags);
    public void dump(FileDescriptor fd, String[] args) throws RemoteException;
    public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException;
 
    BinderProxy() {
        mSelf = new WeakReference(this);
    }
   
    @Override
    protected void finalize() throws Throwable {
            destroy();
    }
   
    private native final void destroy(); 
    private static final void sendDeathNotice(DeathRecipient recipient) {
            recipient.binderDied();
    }
   
    final private WeakReference mSelf;
    private int mObject;
    private int mOrgue;
}

對於Framework層實現,其基本功能都是通過frameworks/base/core/jni目錄裡,可以找到所有JNI實現。對應於Java環境裡Binder實現,我們可以找到frameworks/base/core/jni/android_util_Binder.cpp。可以在這一JNI實現裡看到,實際上所有基於Binder的互動,會通過javaObjectForIBinder()方法基於IBinder引用來建立或是找到對應的Java物件,通過ibinderForJavaObject()通過Java物件找到IBinder引用:

jobject javaObjectForIBinder(JNIEnv* env, constsp<IBinder>& val)
{
    if (val == NULL) return NULL;
    if(val->checkSubclass(&gBinderOffsets)) {
       jobject object = static_cast<JavaBBinder*>(val.get())->object();
       LOGDEATH("objectForBinder%p: it's our own %p!\n", val.get(), object);
       return object;
    }
   AutoMutex _l(mProxyLock);
   jobject object = (jobject)val->findObject(&gBinderProxyOffsets); 1
    if (object != NULL) {
       jobject res = env->CallObjectMethod(object,gWeakReferenceOffsets.mGet);     2
       if(res != NULL) {
           ALOGV("objectForBinder%p: found existing %p!\n", val.get(), res);
           return res;
       }
       LOGDEATH("Proxyobject %p of IBinder %p no longer in working set!!!", object, val.get());
       android_atomic_dec(&gNumProxyRefs);
       val->detachObject(&gBinderProxyOffsets);
       env->DeleteGlobalRef(object);
    }
 
   object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);         3
    if (object != NULL) {
       LOGDEATH("objectForBinder%p: created new proxy %p !\n", val.get(), object);
       env->SetIntField(object, gBinderProxyOffsets.mObject, (int)val.get());
       val->incStrong(object);
       jobject refObject = env->NewGlobalRef(
                env->GetObjectField(object,gBinderProxyOffsets.mSelf));
       val->attachObject(&gBinderProxyOffsets, refObject,
                jnienv_to_javavm(env),proxy_cleanup);
       sp<DeathRecipientList> drl = new DeathRecipientList;
       drl->incStrong((void*)javaObjectForIBinder);
       env->SetIntField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jint>(drl.get()));
       android_atomic_inc(&gNumProxyRefs);
        incRefsCreated(env);
    }
    return object;
}

在javaObjectForIBinder()方法裡,1先會通過傳入的IBinder引用,來查詢是否已經存在所需要使用的BinderProxy物件;2如果存在,則通過BinderProxy的WeakReference引用,然後返回該引用;3如果沒有,則在後面會建立這一BinderProxy物件。除去這幾行,實際上其他程式碼都是在進行引用計數的維護。而在3程式碼的操作裡之後,呼叫incRefsCreated()方法,於是又會索引到另一個BinderInternal物件,使用其ForceGc()方法。

而對於Binder類本身,我們也會看到其init()的native實現,

static void android_os_Binder_init(JNIEnv* env, jobject obj)
{
   JavaBBinderHolder* jbh = new JavaBBinderHolder();
    if (jbh == NULL) {
       jniThrowException(env, "java/lang/OutOfMemoryError",NULL);
       return;
    }
   jbh->incStrong((void*)android_os_Binder_init);
   env->SetIntField(obj, gBinderOffsets.mObject, (int)jbh);
}

有了這三部分的程式碼,我們的Java環境裡的Binder,便與Native態的Binder結合到一起了,最終會得到如下所示的即有Java,也有C++程式碼的複雜關係:


在Java環境裡,某個Binder物件的IBinder引用,則可以通過javaObjectForIBinder()建立或是取得某個已有的BinderProxy物件,BinderProxy物件的transact()是以JNI的方式實現的,於是有可能進一步通過它來構建底層的Binder互動。同時,Java環境裡的Binder物件,會通過init()方法來為該物件建立JavaBBinderHolder,從而使自己被對映到Native環境裡的Binder(在Native環境裡叫BBinder),最終將兩個環境裡的Binder物件被對映到一起。

在這時不光需要完成Binder的實現由Java層轉入Native層,更重要是需要一種機制,可以使Native態發生的變動,再回到Java層。Java語言環境本身提供自動垃圾回收機制,我們需要將Binder物件能夠自動地被Java的GC所管理,同時,我們大部分使用Binder通訊的程式碼還是Java寫的,我們需要能夠在合適的狀態點回調到Javag玩意。JavaBBinderHolder本身只是JavaBBinder的包裝類,在JavaBBinder之上再包裝上引用計數(繼承Refbase),於是同時使用一個能處理GC的BinderInternal便可以自動處理。另外,值得注意的是Java環境裡的Binder類,Binder類提供一個不常見的private的execTransact()方法,這其實就是Binder環境的回撥,JNI程式碼在執行Native環境的onTransact()方法時,會通過回撥Java環境裡的execTransact()方法,從而回調到Java裡實現的onTransact()方法。於是,Java環境裡的Binder,實際上只是一層封裝,直正的Binder通訊是由底層來完成的,由Android系統裡使用C++編寫的一個libBinder庫來完成,如下所示:

libbinder的構成

實現了從Java環境到Native實現的Binder環境之間的互動之後,還需要可以在多個範圍內完成物件的對映。Binder這個概念在Java環境裡大量使用了面向物件的重構技巧,儘可能實現通用操作,而只預留出來需要定製的介面。於是,我們為了支援給Java環境提供的靈活功能,在Native層的Binder,也需要具備面向物件的能力。綜合這兩種需求,我們得到了frameworks/base/libs/binder目錄裡,用C++實現的libbinder(在Android 4.1裡,為了更好地相容NDK被移到frameworks/native/libs/binder)。當然,C++跟Java的分析方式則有不同,C++的標頭檔案與實現,跟C一樣,是分開的,從標頭檔案更容易分析其構成,與實現對應,一般我們都可以在同級目錄裡找到標頭檔案,libbinder的標頭檔案位於frameworks/base/include/binder。

出於跟Java層程式碼進行不同語言空間的對映之用,C++實現的Binder庫也會與Java環境裡的Binder概念有著互通之處。

  • IInterface.h,定義IInterface介面類。與Java的IInterface介面類對應,用於通過統一的asBinder()介面方法返回IBinder引用。但與Java環境不同,C++環境裡需要提供Binder通訊實現,於是IInterface類,在內部被進一步拆分成BpInterface(Proxy端物件)、BnInterface(Stub端物件)兩個模板,從而使用不同模板引數,可以使用對同一IInterface介面的訪問,得到不同功能實現。
  • IBinder.h,定義IBinder介面類。與Java環境的IBinder對應,提供Binder引用,並定義Binder物件所應實現的方法。跟Java環境一樣,IBinder物件會是分別引用到Proxy物件或是Stub物件,通過localBinder()方法返回Binder Stub端的BBinder物件,或是通過remoteBinder()方法返回Binder Proxy端的BpBinder物件。但與Java環境不同,C++環境裡沒有標準的自動化記憶體管理(GabageCollection),於是會內建一個DeathRecipient類,通過Refbase來進行垃圾管理,就像我們前面分析JNI部分,這一管理內容,最終會通過BinderInternal類回撥到Java的自動化記憶體管理。
  • Binder.h,定義BBinder類與BpRefBase類。BBinder類與Java環境裡的Binder類對應,是Binder的Stub實現,通過onTransact()回撥方法接收與處理Binder事件。但它更屬於Binder通訊的基礎類,並不會被直接使用,系統只會通過BBinder類的派生類進行通訊,所以它的類名是BBinder,Base Binder的意思。BBinder裡維護了Extras物件,可線上程完全情況下訪問到BpBinder裡實現的objectManager,從而自動管理Proxy物件。BpRefBase類,用於通過IBinder引用來找到合適的BpBinder類,進一步被應用於BpInterface模板類。
  • BpBinder.h,定義BpBinder類。與Java環境裡的Stub.Proxy物件對應,是Binder的Proxy端實現。作為Proxy端,又是Binder最底層的封裝,這一類的本質作用只是提供一個transact()方法,從而給上層封裝的RPC請求提供最終的Binder通訊介面。與此同時,BpBinder建立的物件,必須與BBinder加入繫結,於是也提供objectManager類,用於提供Proxy端的物件管理,但與BBinder使用單一objectManager物件不同,這裡存在多重對映的可能性,在Proxy物件裡的objectManager會以佇列形式被管理。
  • Parcel.h,定義Parcel類。雖然概念上與Java裡實現序列化的Parcel類是對應的,也提供同樣的操作方法。但在實現上,Native環境裡的Parcel是提供Java環境裡的Parcel概念的一種基礎,所有的Parcel的read|write_*()系列的方法,底層全是由Native程式碼來實現具體的讀寫操作。這也是基於Binder的RPC操作的基礎,我們進行程序間的方法呼叫時,都會基於Parcel來完成引數與返回值的序列化,然後再通過Binder進行傳輸。最終,所有通過實現Parcelable方法得到的物件,都將在傳遞時被對映到Binder驅動所需要的一種平滑過的物件結構,被存入一種叫flat_binder_object的變數裡,從而可以以線性空間的方式儲存並傳輸。

上面的這些基本類,便構成了Binder通訊的基礎。通過這些基本類,不但完成了Java環境與Binder的Native在實現上的概念統一,兩種環境裡的物件都有一一對應的關係。而且這些概念上也通過JNI被關聯到一起,Java環境裡的Binder最終會通過底層的Binder類來進行操作,而底層Binder物件,則會通過RefBase嵌入到Java環境的垃圾回收池。libBinder便擁有了如下所示的構成:


但此時,我們還是沒有解決最核心的問題,這些在Native環境裡進行Binder通訊封裝的類,如何操作Binder驅動來達到通訊的目的。在libbinder的實現裡,我們都會通過兩個專門的類來完成真正的Binder訊息的讀寫操作,ProcessState與IPCThreadState,因為此時已經是全系統唯一的概念了,則不會提供Java程式碼的對映,只由C++程式碼來實現。

  • ProcessState.h,定義ProcessState類。正如這個類的名字所表述的,這個類的作用就是維護與IPC相關的程序狀態,對於任一使用Binder的程序而言,程序空間裡只會有一個ProcessState物件。Binder通訊與其他的IPC機制不同,其他的IPC基本上都是基於檔案描述符fd來標識出不同的通訊過程,而Binder在整個傳輸過程裡都是以程序為單位來進行傳輸。Binder通訊在傳輸時雖然也會使用/dev/binder的檔案描述符,但只是用於與驅動通訊,IPC訊息只以程序為單位進行傳輸,並且也可以通過binder知道程序是否還處於存活狀態,從而可以實現整個系統的垃圾回收。
  • IPCThreadState.h,定義類IPCThreadState。這個類描述以執行緒為基礎的IPC傳輸狀態,Binder通訊的讀寫過程便在這個類裡完成。在Binder提供底層的跨程序通訊能力之後,基於其上建立通訊過程,可以以單執行緒迴圈方式完成,也可以使用多執行緒。如果使用單執行緒,在處理某個大資料量的訊息的收發過程裡容易發生阻塞,資料傳遞的效率降低,同時也會使底層的Binder驅動承載的壓力變大。如果使用簡單多執行緒,有執行緒則建立執行緒加以接收處理,則又產生執行緒維護開銷,並引發多執行緒程式設計失控。在IPCThreadState類的實現裡,是以執行緒池的方式來維護Binder的通訊請求,對於/dev/binder裝置的讀寫請求都是通過執行緒方式並行執行的IPCThreadState的執行緒物件來完成,但並非總是建立,而只是通過IPCThreadState的self()方法取回一個合適的例項來進行處理。

多了這兩個與Binder進行通訊的基本類之後,libbinder的構成便進一步被擴大,但此時已經可以完成Binder通訊,並提供對上層的封裝功能了。於是,從Binder通訊的功能上來看,libbinder的構成實際上是如下如示的樣子:

在libbinder的構成裡,ProcessState,只是一個用於描述程序狀態的類,於是並不直接跟Binder通訊。但Binder驅動會在ProcessState物件裡被開啟,對於程序來說,Binder的使用例項是唯一的,被儲存到mDriverFD裡。同時,由Binder派生出來的遠端物件,也會在ProcessState裡通過getContextObject()和setContextObject()來維護。最後,對於程序當前的執行緒池環境,也是通過ProcessState物件來維護。

至於IPCThreadState類,則實現上會複雜一些,所有的Binder命令,最終都會通過一個IPCThreadState來進行處理。一個IPCThreadState物件,就是通過talkWitheDriver()方法來迴圈地操作ProcessState的mDriverFD,完成Binder驅動的實際讀寫操作。所有命令的傳送操作,都是通過IPCThreadState的transact()方法來完成,而Binder驅動返回過來的訊息,會通過executeCommand()回撥到具體的onTransact()實現。

綜合libbinder的實現,與Java環境的Remote Service,最終得到的Binder通訊便是如下的流程:


Remote Service的訪問者,也就是應用程式程序,會使用Stub.Proxy物件來呼叫transact()方法,而Proxy物件本身會在使用裡建立一個BinderProxy物件,並呼叫這一BinderProxy物件的transact()方法。而Java環境裡的BinderProxy物件的使用,會在libbinder裡建立BpBinder與之對應,BinderProxy的transact()本身是使用JNI實現的,會呼叫BpBinder的transact()實現。這時,在BpBinder的transact()方法裡,Binder命令便會經由IPCThreadState物件的transact()、waiteForResponse()、talkWithDriver(),寫入噹噹前程序維護的mDriverFD,也就是Binder驅動裡。

Remote Service則是Binder命令的接收與處理者,會一直在自己的ProcessState物件維護的mDriverFD上監聽,Remote Service則都使用joinThreadPool()來建立監聽Binder的執行緒。當Binder命令過來之後,監聽的IPCThreadState物件會從talkWithDriver()方法裡返回,讀回相應的命令,然後呼叫executeCommand()方便處理這一命令。在executeCommand()方法裡,當接收到互動請求(命令是BR_TRANSACTION)時,會呼叫Binder命令裡指定BBinder物件的transact()方法。此時,因為Java環境裡Binder物件,在JNI實現裡會自動建立JavaBBinder類,於是BBinder物件的transact()方法,會通過JavaBBinder的onTransact()回撥到Java環境裡Binder物件的execTransact()方法,然後再呼叫到最終的RemoteService裡實現的onTransact()方法。

當然,前面通過aidl程式設計也可以看出,雖然底層實現上如此複雜,九轉十八彎才得到了這樣的遠端呼叫,但對於程式設計使用卻異常簡單,只是需要簡單地使用一個Stub物件即可,所有的底層操作都將系統封裝掉了,在aidl程式設計時甚至都不知道有Proxy物件,全由aidl自動生成。這便是Android的優點之一,無論底層多麼複雜,但上層都是很簡單的介面。而對於系統設計的一個可借鑑之處是,當我們把這套邏輯封裝得非常易於程式設計時,對於底層的改進也將更加平滑,因為使用者並不知道底層發生了怎樣的變動,仍使用同樣的程式設計方式進行呼叫。

這種方式,繞來繞去的,不太容易理解,Java環境與Native環境的互相穿越,也容易迷失方向。這是否有必要呢?如果沒有Java,這種複雜邏輯是沒有理由的,複雜便會效率低下。而且在整個Binder概念的實現上,大量使用面向物件技巧,即便是在C++實現的部分,也會通過虛擬繼承來實現訪問上的靈活性,在嵌入式環境裡使用虛擬繼承來進行動態訪問,本身就是低效之源。但是,因為有了Java語言的需求,這樣設計就很合理了。通過Binder,不但完成了Binder在Java環境與Native環境的成功對映,在Java環境的Binder概念之下通過機器程式碼實現了高效安全的Binder通訊,更進一步的好處是,Binder被託管到了Java虛擬機器的自動化垃圾回收機制裡。假設Android並沒有選擇Java語言,而是選擇了另一種支援垃圾回收的程式語言,我們只需要簡單地將Java態的繫結,也就是Binder概念相關的Java類、JNI實現實現一次即可。

至此,基本上libbinder的基本功能便已經完備了,可以支援上層的基於Binder傳遞物件、使用IBinder引用來進行呼叫遠端程序中的方法、以及通過Parcel來傳遞複雜物件的功能。

除了基本功能之外,libbinder裡還會實現一些C++環境裡的輔助類:

  • 實現在C++環境裡的ServiceManager類,從而在C++環境裡可以直接呼叫getService()方法取得其他System Service
  • 實現IMemory介面方法,從而可以靈活實現多種記憶體共享方式

libbinder裡實現的ServiceManager

ServiceManager本身就是Native Service的一個典型的例子,Java環境裡程式設計會使用ServiceManager,呼叫到SystemServer裡實現的系統功能。而整個Android系統各個組成部分也會不停地進行互動,Native Service之間也會進行相互呼叫。我們可以看到servicemanager是一個C語言寫的獨立程序,Stub端的功能便會由這一servicemanager程序來完成,於是從Binder領域來說,所有以面向物件實現的ServiceManager類,只需要提供Proxy功能。

以Native Service方式來提供ServiceManager功能,則是一個跟我們的前面的ITask很類似一種實現。出於程式碼完整性角度考慮,在IServiceManager裡也提供了BnServiceManager的實現,但由於沒有將這一Service加入到IPCThreadState裡,於是只是空的實現。

在frameworks/native/include/binder/IServiceManager.h定義了以Native方式實現的Service,

namespace android {
    class IServiceManager: publicIInterface   1
    {
    public:
       DECLARE_META_INTERFACE(ServiceManager); 2
       virtual sp<IBinder> getService( const String16& name) const = 0;  3
       virtual sp<IBinder> checkService( const String16& name) const = 0;
       virtual status_t      addService( const String16&name,
                                     constsp<IBinder>& service,
                                     bool allowIsolated =false) = 0;
       virtual Vector<String16>   listServices() =0;
       enum {            4
GET_SERVICE_TRANSACTION=  
IBinder::FIRST_CALL_TRANSACTION,
           CHECK_SERVICE_TRANSACTION,
           ADD_SERVICE_TRANSACTION,
           LIST_SERVICES_TRANSACTION,
       };
    };
   
   sp<IServiceManager> defaultServiceManager();
   
    template<typename INTERFACE>
   status_t getService(const String16& name, sp<INTERFACE>* outService) 5
    {
       const sp<IServiceManager> sm = defaultServiceManager();
       if(sm != NULL) {
           *outService = interface_cast<INTERFACE>(sm->getService(name));
           if((*outService) != NULL) return NO_ERROR;
       }
       return NAME_NOT_FOUND;
    }
   
    boolcheckCallingPermission(const String16& permission);   6
    boolcheckCallingPermission(const String16& permission,
                                int32_t*outPid, int32_t* outUid);
    bool checkPermission(const String16&permission, pid_t pid, uid_t uid);
   
    class BnServiceManager: publicBnInterface<IServiceManager>   7
    {
    public:
       virtual status_t    onTransact(uint32_t code,
                                       const Parcel&data,
                                       Parcel*reply,
                                       uint32_t flags = 0);
    };
}; // namespace android
  1.  IServiceManager這一介面類,繼承自IInterface
  2.  進入IServiceManager的類定義部分,就會通過DECLARE_META_INTERFACE來完成通用部分的定義。
  3.  定義所需要的介面方法,所有介面方法必然會是純虛方法。
  4.  定義IServiceManager所支援的Binder命令,跟Java環境裡的ServiceManager類一致,支援四種命令,分別對應到第3步裡定義的四個介面方法。並且跟其他Native Service一樣,Binder命令都是從IBinder::FIRST_CALL_TRANSACTION開始。
  5.  這裡使用了C++的多型,一般的getService()是直接通過一個Service的名字來取回一個IBinder實現,通過返回的IBinder引用是否為NULL來判斷是否成功完成呼叫。而在這裡定義的getService()方法,進一步封裝了一次,可以返回一個呼叫的狀態值,並且會直接通過interface_cast()巨集來確保Proxy物件被建立。
  6.  checkCallingPermission()、checkPermission()相當於IServiceManager裡提供的一層Proxy功能,這部分的功能是由IPermissionController.cpp來提供,而這部分功能並不適合暴露出來,於是會透過IServiceManager將許可權檢測功能提供出來。
  7.  出於實現上的統一性考慮,在這裡也會提供BnServiceManager的實現。

有了這個IServiceManager介面類的定義,在IServiceManager實現就跟

sp<IServiceManager>defaultServiceManager()     1
{
    if(gDefaultServiceManager != NULL) return gDefaultServiceManager;
    {
       AutoMutex _l(gDefaultServiceManagerLock);
       if(gDefaultServiceManager == NULL) {
           gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));    2
       }
    }
    returngDefaultServiceManager;
}
…
class BpServiceManager : public BpInterface<IServiceManager> 3
{
public:
   BpServiceManager(const sp<IBinder>& impl)
       : BpInterface<IServiceManager>(impl)
    {
}
…
    virtualsp<IBinder> checkService( const String16& name) const    4
    {
       Parcel data, reply;
       data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
       remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
       return reply.readStrongBinder();
}
     …
IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); 5
…
}
  1. defaultServiceManager(),每個程序都需要通過getService()來取回RemoteService,所以必然需要呼叫到defaultServiceManager()來取回一個IServiceManager的Proxy物件。
  2. 在defaultServiceManager()裡使用了Singleton模式,對於某個程序只會建立一個唯一的IServiceManager的Proxy例項。值得注意的是getContextObject()會通過一個handle變數來取得一個IBinder引用,然後再通過interface_cast()轉換成IServiceManager介面類。getContextObject()方法的引數會是整型的handle,handle的值會隨著不同Service的引用,每使用一個,handle的值便會加1,這樣每個程序空間會擁有各自不同的handle值,使得基於Binder通訊的安全變高,不容易簡單地抓取Binder命令來分析程式的行為。這裡使用的handle值為0,於是對於每個程序,handle為0的Service都會是IServiceManager。
  3. 實現IServiceManager的Proxy類,於是實現上便會繼承自BpInterface的模板。
  4. 跟Java環境的ServiceManager一致,getService()內部也會是呼叫checkService()來取得Service的IBinder引用。所有的Proxy端方法,都使用統一的模式,就是將呼叫的方法轉換成Binder命令,然後傳送出去。
  5. IMPLEMENT_META_INTERFACE()巨集,會實現通用部分的程式碼,同時也會設定特定的descriptor。

       通過這樣的方式,最終就得到了簡單而且高效的,並且與ServiceManager.java實現並行的ServiceManager的Proxy端。

通過libbinder靈活使用記憶體

在我們傳統的Unix/Linux程式設計環境裡,為了保持程序之間的互相獨立性,都是使用者程序空間裡的內容互相獨立,使用不同的記憶體對映,核心空間便會共享同一份內容,使用同一份記憶體對映。這樣的情況下,如果需要在程序之間傳遞資料,必然是先通過copy_from_user()系統呼叫從使用者空間先拷貝到核心空間,然後再在另一個程序空間裡通過copy_from_user()系統呼叫從核心空間拷貝回用戶空間。這種方式就保證了不同程序可以傳遞資料,同時程序之間互相不會有干擾,任一程序出錯,對另一程序完全沒有影響,因為程序空間本身是獨立的。在Linux核心之上,任何IPC機制都採用了這種實現,Binder也不例外。比如Remote Service,在任一互動過程裡,資料始終會有三份,核心一份、程序空間各一份拷貝:


但這種方式比較低效,多次記憶體拷貝會造成記憶體的開銷,同時由於copy_from_user()和copy_to_user()系統呼叫,會造成計算上的開銷。假設已經確定記憶體的傳遞時一方只讀,此時就可以改進記憶體的使用,在發生記憶體寫請求的程序裡,仍然使用copy_from_user(),而讀取記憶體的部分,則不再使用copy_to_user(),而是通過mmap()直接對映一段記憶體到自己的程序空間內,這樣只會發生一次記憶體拷貝。


最後,還有一種可能性,所有程序都共享同一塊記憶體區域,不管記憶體裡儲存的內容發生了任何改變,所有程序都應該見得到。這種使用在Linux程式設計裡也很普遍,但一般只用於驅動訪問,很少被用於IPC程序間通訊。這種使用情境下,如果所共享的頁源自於使用者空間,則難以保證在物理分配上的連續性,更重要的是不要求,如果承載記憶體頁的程序掛掉,則所有涉及互動的程序都不得不掛掉,所以記憶體頁要在核心空間裡。同時,在核心態則需要有一定的實體存放這樣的頁面對映引用,IPC機制並不支援這樣的頁面對映管理。但Binder IPC不受限於此,Binder驅動實際上只是一個基於記憶體對映的虛擬字元裝置,於是可以天然地在傳輸過程裡使用記憶體頁的對映功能。於是,又有了第三種使用記憶體頁的方式,就是靈活地記憶體對映功能:


如上所示,記憶體頁實際上是在使用時通過核心態來分配的,記憶體只在核心態有一份拷貝,所有的使用者程序都只通過mmap()系統呼叫來引用這樣的頁。但是這樣的方式會引發記憶體管理上的混亂,核心空間的頁面在分配上的優先順序要遠大於使用者空間,而且必須以連續物理頁來分配,也容易造成記憶體使用上的失控。於是,Android在原有的記憶體管理機制上拓展出來兩種“特殊”記憶體管理機制,準確地說是共享記憶體的實現機制,pmem和ashmem,這兩種機制可以將核心裡記憶體分配機制暴露到使用者態來操作。Pmem是一種預留出來一段實體記憶體供使用者態分配連續實體記憶體來使用,而ashmem則是Android對原有的Shared Memory(共享記憶體機制)的一種改進,這兩種機制我們後臺再詳細。雖然這種共享記憶體方式本身跟Binder機制沒有關係的,並非由Binder IPC來實現這樣共享功能,但得益於Binder通訊與Java垃圾回收的繫結,於是Binder為這種記憶體共享提供了自動的記憶體管理功能。

  • IMemory.h,定義兩個基於IBinder的遠端類,生成兩個遠端介面類,IMemory與IMemoryHeap,分別用於使用記憶體和分配記憶體。這是Android在系統設計上靈活性的一種體現,我們也知道IBinder是提供遠端引用的,對於記憶體使用兩個基於IBinder的介面類,則意味著申請記憶體與分配記憶體可分別由單獨程序來提供。試想一下,比如一個照相機應用程式,通過IMemory介面向mediaserver程序申請記憶體,但在mediaserver程序裡發出記憶體申請之後,則會由負責顯示的surfaceflinger來負責分配所需要的空間。這樣複雜的供給關係,只取決於程式碼如何實現,也不受限於程序空間,而且不會影響效率,因為無論多少程序互動,在底層實際上還是隻有一份拷貝。當然,同時使用IMemory與IMemoryHeap遠端介面,並不一定要發生在多程序環境裡,當兩個介面都在同一程序空間裡提供Proxy與Stub時,實際上只是多繞了一層Binder IPC,但還是當前程序空間內,也不影響效率。另外,這兩個介面類,出於效能的考慮,會是以Native Service的方式來提供。
  • MemoryDealer.h,定義一個預設的分配記憶體的MemoryDealer類。在這一類裡內建了一個最簡化的列表式記憶體管理演算法,這樣需要提供記憶體分配部分,可以使用這一類來建立記憶體空間,並通過IMemoryHeap共享出去。
  • MemoryBase.h,通過MemoryBase類來提供IMemory介面類的Stub端實現。MemoryBase會通過getMemory()的遠端方法來返回一個IMemoryHeap例項,同時也會是MemoryDealer的操作物件。命名為Base,就說明這會是該型別的基類定義,如果有其他使用IMemory介面的地方可以繼承該類,再派出生新的特性。
  • MemoryHeapBase.h,通過MemoryHeapBase類來實現IMemoryHeap介面類的Stub端。在這個類裡實現可用於遠端共享的堆空間分配,如果不用於遠端共享,則直接使用malloc()即可,IMemoryHeap介面相當於是遠端實現的malloc(),new()等操作方法。在這一方法裡會定義一些基本的Heap分配操作,根據不同的應用情境分配不同型別的記憶體(基於檔案的、匿名空間等)。
  • MemoryHeapPmem.h,實現基於Pmem機制並實現IMemoryHeap介面的MemoryHeapHmem類。如果有特殊記憶體堆的使用需求,則可以通過MemoryHeadBase類派生出新的MemoryHead類,比如在MemoryHeapPmem。所有的的特殊

最終,針對記憶體的分配與使用,在底層也會靈活地組織起來,在記憶體使用“食物鏈”最上流的使用記憶體的部分,只需要呼叫MemoryBase物件的getMemory()介面方法,這一請求會通過IMemory介面轉發到能處理該請求的另一程序。而getMemory()又會返回一個IMemoryHeap引用,這樣會自動地又將請求分發到可以處理這一IMemoryHeap分配的程序來處理記憶體分配。於是,在libbinder實現裡針對跨程序的記憶體使用與分配,又可以得到如下的關係圖:


有了IMemory這樣的介面類之後,在Android系統裡就可以更靈活地定義不同的記憶體使用方法。