Service与Android系统设计(5)-- libbinder

特别声明:本系列文章LiAnLab.org著作权所有,转载请注明出处。作者系LiAnLab.org资深Android技术顾问吴赫老师。本系列文章交流与讨论:@宋宝华Barry


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()回调接收。这样便构成了交互的能力。

Service与Android系统设计(5)-- libbinder_第1张图片

但从Java环境里,实际上,我们根本不知道是如何处理完成的,我们跟踪所有的Binder相关的Java实现,也不看不出来。比如,我们在客户端经常会使用这样的方法来写Proxy端方法:

[java]  view plain copy
  1.     public IBinder getService(String name) throws RemoteException {   
  2.        Parcel data = Parcel.obtain();  
  3.        Parcel reply = Parcel.obtain();  
  4.        data.writeInterfaceToken(IServiceManager.descriptor);  
  5.        data.writeString(name);  
  6.        mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);  
  7.        IBinder binder = reply.readStrongBinder();  
  8.        reply.recycle();  
  9.        data.recycle();  
  10.        return binder;  
  11. }  

这种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接口类,于是它拥有如下的结构:

[java]  view plain copy
  1. public class Binder implements IBinder {  
  2.     public static final native int getCallingPid();  
  3.     public static final native int getCallingUid();  
  4.     public static final int getOrigCallingUid();  
  5.     private static final native int getOrigCallingUidNative();  
  6.     public static final int getOrigCallingUser();  
  7.      
  8.     public static final native long clearCallingIdentity();  
  9.     public static final native void restoreCallingIdentity(long token);  
  10.     public static final native void setThreadStrictModePolicy(int policyMask);  
  11.     public static final native int getThreadStrictModePolicy();  
  12.     public static final native void flushPendingCommands();  
  13.     public static final native void joinThreadPool();  
  14.      
  15.     public Binder() {  
  16.         init();  
  17.         ...  
  18.     }  
  19.      
  20.     public void attachInterface(IInterface owner, Stringdescriptor) ;  
  21.     public String getInterfaceDescriptor();  
  22.     public boolean pingBinder() ;  
  23.     public boolean isBinderAlive();  
  24.     public IInterface queryLocalInterface(Stringdescriptor);  
  25.     protected boolean onTransact(int code, Parcel data, Parcel reply,  
  26.                                  int flags) throws RemoteException;  
  27.     public void dump(FileDescriptor fd, String[] args);  
  28.    
  29.     protected void dump(FileDescriptor fd, PrintWriter fout,String[] args);  
  30.     public final boolean transact(int code, Parcel data, Parcel reply,  
  31.                                   int flags) throws RemoteException;  
  32.    
  33.     public void linkToDeath(DeathRecipient recipient, int flags) ;  
  34.     public boolean unlinkToDeath(DeathRecipient recipient, int flags);  
  35.      
  36.     protected void finalize() throws Throwable {  
  37.             destroy();  
  38.     }  
  39.      
  40.     private native final void init();  
  41.     private native final void destroy();  
  42.    
  43.     private boolean execTransact(int code, int dataObj, int replyObj,  
  44.             int flags) {  
  45.         res = onTransact(code, data, reply,flags);  
  46.         return res;  
  47.     }  
  48. }  

作为一个继承至IBinder接口的类,Binder这个类势必要实现所有的接口方法。但比较特殊的是Binder类的构造方法Binder()会调用到一个init()方法,析构方法finalize()会调用到一个destroy()方法,这两个方法并不是由Java实现,而是被标识为native的JNI实现的方法。另外需要注意到的是,Binder类本身实现了transact()与onTransact()收发两端的代码,但只是进程类的直接交互,并不会进程间的Binder交互。

另外,我们在同一Binder.java类里,还可以找到另一个BinderProxy类的定义,在这一类里会使用一个native标识的transact()方法。虽然在源代码里找不到任何使用这一类的地方,但从命名方式上来看,这一类应该会是被创建的默认的Proxy端。BinderProxy.java的定义如下:

[java]  view plain copy
  1. final class BinderProxy implements IBinder {  
  2.     public native boolean pingBinder();  
  3.     public native boolean isBinderAlive();  
  4.     public IInterface queryLocalInterface(Stringdescriptor);  
  5.     public native String getInterfaceDescriptor() throws RemoteException;  
  6.     public native boolean transact(int code, Parcel data, Parcel reply,  
  7.             int flags) throws RemoteException;  
  8.     public native void linkToDeath(DeathRecipient recipient,int flags)  
  9.             throws RemoteException;  
  10.     public native boolean unlinkToDeath(DeathRecipient recipient,int flags);  
  11.     public void dump(FileDescriptor fd, String[] args) throws RemoteException;  
  12.     public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException;  
  13.    
  14.     BinderProxy() {  
  15.         mSelf = new WeakReference(this);  
  16.     }  
  17.      
  18.     @Override  
  19.     protected void finalize() throws Throwable {  
  20.             destroy();  
  21.     }  
  22.      
  23.     private native final void destroy();   
  24.     private static final void sendDeathNotice(DeathRecipient recipient) {  
  25.             recipient.binderDied();  
  26.     }  
  27.      
  28.     final private WeakReference mSelf;  
  29.     private int mObject;  
  30.     private int mOrgue;  
  31. }  

对于Framework层实现,其基本功能都是通过frameworks/base/core/jni目录里,可以找到所有JNI实现。对应于Java环境里Binder实现,我们可以找到frameworks/base/core/jni/android_util_Binder.cpp。可以在这一JNI实现里看到,实际上所有基于Binder的交互,会通过javaObjectForIBinder()方法基于IBinder引用来创建或是找到对应的Java对象,通过ibinderForJavaObject()通过Java对象找到IBinder引用:

[cpp]  view plain copy
  1. jobject javaObjectForIBinder(JNIEnv* env, constsp<IBinder>& val)  
  2. {  
  3.     if (val == NULL) return NULL;  
  4.     if(val->checkSubclass(&gBinderOffsets)) {  
  5.        jobject object = static_cast<JavaBBinder*>(val.get())->object();  
  6.        LOGDEATH("objectForBinder%p: it's our own %p!\n", val.get(), object);  
  7.        return object;  
  8.     }  
  9.    AutoMutex _l(mProxyLock);  
  10.    jobject object = (jobject)val->findObject(&gBinderProxyOffsets); 1  
  11.     if (object != NULL) {  
  12.        jobject res = env->CallObjectMethod(object,gWeakReferenceOffsets.mGet);     2  
  13.        if(res != NULL) {  
  14.            ALOGV("objectForBinder%p: found existing %p!\n", val.get(), res);  
  15.            return res;  
  16.        }  
  17.        LOGDEATH("Proxyobject %p of IBinder %p no longer in working set!!!", object, val.get());  
  18.        android_atomic_dec(&gNumProxyRefs);  
  19.        val->detachObject(&gBinderProxyOffsets);  
  20.        env->DeleteGlobalRef(object);  
  21.     }  
  22.    
  23.    object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);         3  
  24.     if (object != NULL) {  
  25.        LOGDEATH("objectForBinder%p: created new proxy %p !\n", val.get(), object);  
  26.        env->SetIntField(object, gBinderProxyOffsets.mObject, (int)val.get());  
  27.        val->incStrong(object);  
  28.        jobject refObject = env->NewGlobalRef(  
  29.                 env->GetObjectField(object,gBinderProxyOffsets.mSelf));  
  30.        val->attachObject(&gBinderProxyOffsets, refObject,  
  31.                 jnienv_to_javavm(env),proxy_cleanup);  
  32.        sp<DeathRecipientList> drl = new DeathRecipientList;  
  33.        drl->incStrong((void*)javaObjectForIBinder);  
  34.        env->SetIntField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jint>(drl.get()));  
  35.        android_atomic_inc(&gNumProxyRefs);  
  36.         incRefsCreated(env);  
  37.     }  
  38.     return object;  
  39. }  

在javaObjectForIBinder()方法里,1先会通过传入的IBinder引用,来查找是否已经存在所需要使用的BinderProxy对象;2如果存在,则通过BinderProxy的WeakReference引用,然后返回该引用;3如果没有,则在后面会创建这一BinderProxy对象。除去这几行,实际上其他代码都是在进行引用计数的维护。而在3代码的操作里之后,调用incRefsCreated()方法,于是又会索引到另一个BinderInternal对象,使用其ForceGc()方法。

而对于Binder类本身,我们也会看到其init()的native实现,

[cpp]  view plain copy
  1. static void android_os_Binder_init(JNIEnv* env, jobject obj)  
  2. {  
  3.    JavaBBinderHolder* jbh = new JavaBBinderHolder();  
  4.     if (jbh == NULL) {  
  5.        jniThrowException(env, "java/lang/OutOfMemoryError",NULL);  
  6.        return;  
  7.     }  
  8.    jbh->incStrong((void*)android_os_Binder_init);  
  9.    env->SetIntField(obj, gBinderOffsets.mObject, (int)jbh);  
  10. }  

有了这三部分的代码,我们的Java环境里的Binder,便与Native态的Binder结合到一起了,最终会得到如下所示的即有Java,也有C++代码的复杂关系:

Service与Android系统设计(5)-- libbinder_第2张图片

在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库来完成,如下所示:

Service与Android系统设计(5)-- libbinder_第3张图片

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便拥有了如下所示的构成:

Service与Android系统设计(5)-- libbinder_第4张图片

但此时,我们还是没有解决最核心的问题,这些在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的构成实际上是如下如示的样子:

Service与Android系统设计(5)-- libbinder_第5张图片

在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通信便是如下的流程:

Service与Android系统设计(5)-- libbinder_第6张图片

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,

[cpp]  view plain copy
  1. namespace android {  
  2.     class IServiceManager: publicIInterface   1  
  3.     {  
  4.     public:  
  5.        DECLARE_META_INTERFACE(ServiceManager); 2  
  6.        virtual sp<IBinder> getService( const String16& name) const = 0;  3  
  7.        virtual sp<IBinder> checkService( const String16& name) const = 0;  
  8.        virtual status_t      addService( const String16&name,  
  9.                                      constsp<IBinder>& service,  
  10.                                      bool allowIsolated =false) = 0;  
  11.        virtual Vector<String16>   listServices() =0;  
  12.        enum {            4  
  13. GET_SERVICE_TRANSACTION=    
  14. IBinder::FIRST_CALL_TRANSACTION,  
  15.            CHECK_SERVICE_TRANSACTION,  
  16.            ADD_SERVICE_TRANSACTION,  
  17.            LIST_SERVICES_TRANSACTION,  
  18.        };  
  19.     };  
  20.      
  21.    sp<IServiceManager> defaultServiceManager();  
  22.      
  23.     template<typename INTERFACE>  
  24.    status_t getService(const String16& name, sp<INTERFACE>* outService) 5  
  25.     {  
  26.        const sp<IServiceManager> sm = defaultServiceManager();  
  27.        if(sm != NULL) {  
  28.            *outService = interface_cast<INTERFACE>(sm->getService(name));  
  29.            if((*outService) != NULL) return NO_ERROR;  
  30.        }  
  31.        return NAME_NOT_FOUND;  
  32.     }  
  33.      
  34.     boolcheckCallingPermission(const String16& permission);   6  
  35.     boolcheckCallingPermission(const String16& permission,  
  36.                                 int32_t*outPid, int32_t* outUid);  
  37.     bool checkPermission(const String16&permission, pid_t pid, uid_t uid);  
  38.      
  39.     class BnServiceManager: publicBnInterface<IServiceManager>   7  
  40.     {  
  41.     public:  
  42.        virtual status_t    onTransact(uint32_t code,  
  43.                                        const Parcel&data,  
  44.                                        Parcel*reply,  
  45.                                        uint32_t flags = 0);  
  46.     };  
  47. }; // 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实现就跟

[cpp]  view plain copy
  1. sp<IServiceManager>defaultServiceManager()     1  
  2. {  
  3.     if(gDefaultServiceManager != NULL) return gDefaultServiceManager;  
  4.     {  
  5.        AutoMutex _l(gDefaultServiceManagerLock);  
  6.        if(gDefaultServiceManager == NULL) {  
  7.            gDefaultServiceManager = interface_cast<IServiceManager>(  
  8.                 ProcessState::self()->getContextObject(NULL));    2  
  9.        }  
  10.     }  
  11.     returngDefaultServiceManager;  
  12. }  
  13. …  
  14. class BpServiceManager : public BpInterface<IServiceManager> 3  
  15. {  
  16. public:  
  17.    BpServiceManager(const sp<IBinder>& impl)  
  18.        : BpInterface<IServiceManager>(impl)  
  19.     {  
  20. }  
  21. …  
  22.     virtualsp<IBinder> checkService( const String16& name) const    4  
  23.     {  
  24.        Parcel data, reply;  
  25.        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());  
  26.         data.writeString16(name);  
  27.        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);  
  28.        return reply.readStrongBinder();  
  29. }  
  30.      …  
  31. IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); 5  
  32. …  
  33. }  

  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,在任一交互过程里,数据始终会有三份,内核一份、进程空间各一份拷贝:

Service与Android系统设计(5)-- libbinder_第7张图片

但这种方式比较低效,多次内存拷贝会造成内存的开销,同时由于copy_from_user()和copy_to_user()系统调用,会造成计算上的开销。假设已经确定内存的传递时一方只读,此时就可以改进内存的使用,在发生内存写请求的进程里,仍然使用copy_from_user(),而读取内存的部分,则不再使用copy_to_user(),而是通过mmap()直接映射一段内存到自己的进程空间内,这样只会发生一次内存拷贝。

Service与Android系统设计(5)-- libbinder_第8张图片

最后,还有一种可能性,所有进程都共享同一块内存区域,不管内存里保存的内容发生了任何改变,所有进程都应该见得到。这种使用在Linux编程里也很普遍,但一般只用于驱动访问,很少被用于IPC进程间通信。这种使用情境下,如果所共享的页源自于用户空间,则难以保证在物理分配上的连续性,更重要的是不要求,如果承载内存页的进程挂掉,则所有涉及交互的进程都不得不挂掉,所以内存页要在内核空间里。同时,在内核态则需要有一定的实体存放这样的页面映射引用,IPC机制并不支持这样的页面映射管理。但Binder IPC不受限于此,Binder驱动实际上只是一个基于内存映射的虚拟字符设备,于是可以天然地在传输过程里使用内存页的映射功能。于是,又有了第三种使用内存页的方式,就是灵活地内存映射功能:

Service与Android系统设计(5)-- libbinder_第9张图片

如上所示,内存页实际上是在使用时通过内核态来分配的,内存只在内核态有一份拷贝,所有的用户进程都只通过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实现里针对跨进程的内存使用与分配,又可以得到如下的关系图:

Service与Android系统设计(5)-- libbinder_第10张图片

有了IMemory这样的接口类之后,在Android系统里就可以更灵活地定义不同的内存使用方法。

你可能感兴趣的:(Service与Android系统设计(5)-- libbinder)