获取Binder Server 的过程

来源:互联网 发布:mac散热 编辑:程序博客网 时间:2024/06/01 19:36

相关文件参考:

点击打开链接 http://blog.csdn.net/lin20044140410/article/details/50951260

点击打开链接http://blog.csdn.net/lin20044140410/article/details/51106372

Binder中一些类的关系:

要访问一个Binderserver,比如ServiceManager,流程有以下几步:

ServiceManagerbinder的服务管理者,同时它本身也是一个Binderserver

1)打开binder设备

2)执行mmap

3)通过Binder驱动想servicemanager发送请求

4)获得返回的结果


首先,每一个binderclient都亲自执行以上几个步骤获取SM的服务,定会浪费时间,如果app代码中每次使用SM服务,都要执行一次打开Binder设备,执行mmap,定会消耗较多系统资源,所以定会有一个封装,即是ProcessState,来专门管理每个appBinder操作,而且保证,

每一个进程只打开一次binder设备,只做一次内存映射,所有需要binder驱动的线程都共享这一资源;因为每一个线程都有与Binder驱动自由沟通的权利,所以还有一个封装是
IPCThreadState
,负责与Binder进行实际的命令通信。实际上
java层并没有直接使用这两个类,而是通过BpBinder间接使用ProcessStateIPCThreadState完成与Binder驱动的通信


尽管如此,如果app借助于BpBinderProcessStateIPCThreadState来跟Binder通信,还是需要发送BINDER_WRITE_READ等具体命令,还是过于繁琐,所以有了更进一步的封装,ServiceManagerProxy,在ServiceManagerProxy又加了一层封装servicemanager.java,因为servicemanager.java中所有接口都是static的,所以不需要额外创建类对象就可以直接使用SM的功能。



那么ServiceManagerProxy又是如何跟binder通信的呢?这里就借助了IBinder对象,它是跟底层Binder沟通的工具,因为Binder既要面向java层应用,还要面向native层实现,所以IBinder这个接口,既有java层的实现,也有native层的实现。native层的实现就是BpBinder(BpBinder.cpp)java层的是Binder.java中的BinderProxy


ServiceManagerProxy会把请求发送给BinderProxyBinderProxy会通过native接口,把请求发到BpBinder.cpp,进一步通过IPCThreadState发给Binder驱动



比如要获取常用的系统服务AMS,方法是:IBinder b = ServiceManager.getService("activity"),下面分析

ServiceManager.getService()的实现,及进程间数据时怎么共享的。

 

getService的返回值其实是一个指针sp,指向一个Bpbinder,被强类型转换成了IBinder,Bpbinder的构造函数的参数就是就是查询出来的BinderServer的句柄值。

Servicemanager本身也是一个Binderserver,只不过它的句柄值是0,所以任何binder client都可以通过0这个句柄值来构建一个BpBinder。

getService()@ServiceManager.java

public staticIBinder getService(String name) {

           returngetIServiceManager().getService(name);

}

首先要获取ServiceManagerProxy的句柄,通过ServiceManagerProxy获取ServiceManager提供的服务。

private staticIServiceManager getIServiceManager() {

           IServiceManager sServiceManager;

           sServiceManager =

                    ServiceManagerNative.asInterface(BinderInternal.getContextObject());

           return sServiceManager;

}

Step1,在进入ServiceManagerNative类之前,先看BinderInternal.getContextObject()的返回值是一个IBinder,实际跟Binder驱动通信的就是这个IBinderIBinder是一个接口,并且在native层和java层都有定义,分别是:IBinder.hIBinder.java,这个BinderInternal主要的功能就是提供一个接口,方便获取IBinder对象:

public staticfinal native IBinder getContextObject()@BinderInternal.java;

而且getContextObjectnative函数,因为跟Binder驱动通信,最终是要通过JNI调用本地代码来实现。但是ServiceManagerNative.asInterface(…)这里需要的是一个java层的Ibinder对象,所以必然先获取一个native层的Ibinder,然后把nativeIbinder转成java层的Ibinder,来看具体实现:

android_os_BinderInternal_getContextObject()@android_util_Binder.cpp

这是getContextObject对应的native层实现。

static jobjectandroid_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz){

           sp<IBinder> b =ProcessState::self()->getContextObject(NULL);

           return javaObjectForIBinder(env, b);

}

可以看到native层的Ibinder是通过ProcessState获取的,在与Binder驱动的通信中,ProcessState承担了打开Binder设备节点,执行mmap内存映射的工作。

sp<IBinder>ProcessState::getContextObject(const sp<IBinder>&)@ProcessState.cpp{

           return getStrongProxyForHandle(0);

}

sp<IBinder>ProcessState::getStrongProxyForHandle(int32_t handle){

           b = new BpBinder(handle);

}

可以看到这里创建了BpBinder对象,对应了Ibindernative层的实现。这里getStrongProxyForHandle()的参数0代表Service manager,handle也就表示了这个proxy是属于谁的,比如0就表示这个proxy属于SM。接下来通过javaObjectForIBinderBpBinder转成了IBinderJava层实现,

jobjectjavaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)@android_util_Binder.cpp{

           jobject object =(jobject)val->findObject(&gBinderProxyOffsets);

           object =env->NewObject(gBinderProxyOffsets.mClass,

                    gBinderProxyOffsets.mConstructor);

           env->SetLongField(object,gBinderProxyOffsets.mObject, (jlong)val.get());

}

这个javaIbinder实现类是谁呢?就要看gBinderProxyOffsets.mClass是什么值?

const char*const kBinderProxyPathName = "android/os/BinderProxy";

static intint_register_android_os_BinderProxy(JNIEnv* env)@android_util_Binder.cpp{

           jclass clazz = FindClassOrDie(env,"java/lang/Error");

           clazz = FindClassOrDie(env,kBinderProxyPathName);

           gBinderProxyOffsets.mClass =MakeGlobalRefOrDie(env, clazz);

}

可以看出这里Java层的Ibinder实现类是BinderProxy,这个类在Binder.java中,所谓的转化就是通过JNINewObject创建了一个BinderProxy对象,并且让这个Proxy持有了一个nativeIbinder的引用,也就是这句代码的作用:

env->SetLongField(object,gBinderProxyOffsets.mObject, (jlong)val.get());

这里持有这个引用的一个作用就是做反向转化,即是proxy把请求传给binder驱动时,会再把binderProxy转成BpBinder

到这里分析完了如何获取Ibinder对象。下面看如何通过Ibinder获取SM的服务。

Step2getIServiceManager()@ServiceManager.java{

         sServiceManager= ServiceManagerNative.asInterface(BinderInternal.getContextObject());

}

第一步的分析是其中BinderInternal.getContextObject(),接下来看ServiceManagerNative的处理。

static public IServiceManager asInterface(IBinderobj)@ServiceManagerNative.java{

         returnnew ServiceManagerProxy(obj);

}

这里new了一个ServiceManagerProxy,做为Servicemanager的代理,必然要跟Binder驱动通信,所以构造函数的参数中传了Ibinder对象,当然这是第一次访问,后面如果有了缓存后,不会再创建ServiceManagerProxy了。

private IBinder mRemote;

public ServiceManagerProxy(IBinder remote){

         mRemote= remote;

}

下面看ServiceManagerProxygetService的处理:

public IBinder getService(String name)throws RemoteException {

         Parceldata = Parcel.obtain();

         Parcelreply = Parcel.obtain();

         data.writeInterfaceToken(IServiceManager.descriptor);

         data.writeString(name);

         mRemote.transact(GET_SERVICE_TRANSACTION,data, reply, 0);

         IBinderbinder = reply.readStrongBinder();

         reply.recycle();

         data.recycle();

         returnbinder;

}

先是Parcel打包准备数据,核心的语句就是mRemote.transact(),最后获取结果,transact之后Binder驱动会把调用者所在进程挂起,直到有了结果在把它唤醒,这个挂起的操作是binder驱动在执行具体命令时通过调用wait_event_interruptible操作的。等服务端处理完成,写入返回数据时通过wake_up_interruptible(&thread->wait);再唤醒被挂起的进程。根据第一步的分析,这个mRemote就是BinderProxy,接着看BinderProxytransact函数。

final class BinderProxy implements IBinder{

public booleantransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException{

         return transactNative(code, data,reply, flags);

}

}

这里是一个native接口transactNative,实现在android_util_Binder.cpp

jboolean android_os_BinderProxy_transact()@android_util_Binder.cpp{

         IBinder*target = (IBinder*) env->GetLongField(obj, gBinderProxyOffsets.mObject);

         status_terr = target->transact(code, *data, reply, flags);

}

先做了一个反向转化,获得BpBinder,最会通过BpBinder.transact处理用户的Binder请求。

status_t BpBinder::transact()@BpBinder.cpp{

         status_tstatus = IPCThreadState::self()->transact(mHandle, code, data, reply,flags);

}

可以看到实际跟binder驱动做命令交互的是IPCThreadState。整个binder的使用过程,不管怎么复杂,最后都还是通过IPCThreadstateProcessState来实现的,其中ProcessState负责打开dev/binder/设备节点,执行mmap,IPCThreadstate负责跟Binder驱动做实际的命令交互。

 

Step3,分析下进程间数据时怎么共享的。

ServiceManager.getService()这个过程为例,一个是client端进程,一个是server段进程,这里的Server段就是ServiceManager所在进程,一个需要IPC的进程在启动的时候除了打开"/dev/binder",还要做mmapServiceManager也是这样的,具体可以看service_manager.cmain函数。

struct binder_state

{

   int fd;

   void *mapped;

   size_t mapsize;

};

struct binder_state *bs;

bs->mapped = mmap(NULL, mapsize,PROT_READ, MAP_PRIVATE, bs->fd, 0);

ServiceManager来说,可以看到通过mmap()返回值得到了一个内存地址,这个是虚拟地址,这个地址通过虚拟内存转换后最终指向物理内存的某个位置。

Binder驱动来说,它也有一个指针指向某个虚拟内存地址,以bwr->buffer表示吧,经过虚拟内存转换后,跟ServiceManager中指向的物理内存处于同一个位置。

这样BinderServiceManager就拥有了若干公用的物理内存块。他们对各自内存的操作,实际是在同一块内存中执行的。

那么现在有一个client端进程想要获取ServiceManager的服务,比如getService(),这个请求在传到binder驱动后,Binder驱动通过copy_from_userclient端进程的某段数据复制到他的bwr->buffer指向的内存空间中,这个copy_from_user的调用是在binder_ioctl()@Binder.c中发生的,这个Binder.cBinder驱动的文件,具体路径:kernel/drivers/staging/android/Binder.c,要跟ServiceManagerBinder.c区分开。

前面说过,这个bwr->buffer实际指向的物理内存跟ServiceManager进程时共享的,所以ServiceManager就可以直接访问到这段数据了。也就是说,Binder驱动用了一次复制,实现了client端和server端两个进程间的数据共享。


总结一个知识点,作为android新造的一个IPC机制Binder,怎么体现进程间通信的效率的?

核心的一点是利用了mmap这个系统调用,mmap(memory map)可以将某个设备或者文件映射到应用进程的内存空间中,这样访问应用中的这块内存就相当于对设备或文件进行读写,而且不需要在通过read(),write()了。那么mmap应用到进程间通信,就是通过映射一块物理内存来共享内存,这种方式因为减少了数据复制的次数,在一定程度上提高了进程间通信的效率,那么是怎么减少内存复制次数的呢?

假设有两个进程PA,PB,其中进程PB通过open(),mmap()后与binder驱动建立了联系,如下图:


从上图,对于应用程序来说,通过mmap()返回一个内存地址(这是个虚拟地址),经过转换最终会指向物理内存的某个位置。

对Binder驱动来说,它也有一个指针binder_proc->buffer,指向某个虚拟内存地址,经过转换和应用程序中指向的物理内存处在同一个位置。

这样,binder和应用程序就拥有了一些共用的物理内存块。

再看进程PA进来后发生的变化:


在进程PA这侧,binder驱动通过copy_from_user(),把进程PA的某段数据复制到其binder_proc->buffer指向的内存空间,因为这块空间在物理内存中的位置和进程PB是共享的,所以进程PB就可以直接访问到这段来自PA的数据了。

Binder驱动只用一次复制,就是实现了进程PA,PB间的数据共享。

 

阅读全文
0 0