获取Binder Server 的过程
相关文件参考:
点击打开链接 http://blog.****.net/lin20044140410/article/details/50951260
点击打开链接http://blog.****.net/lin20044140410/article/details/51106372
Binder中一些类的关系:
要访问一个Binderserver,比如ServiceManager,流程有以下几步:
ServiceManager是binder的服务管理者,同时它本身也是一个Binderserver。
1)打开binder设备
2)执行mmap
3)通过Binder驱动想servicemanager发送请求
4)获得返回的结果
首先,每一个binderclient都亲自执行以上几个步骤获取SM的服务,定会浪费时间,如果app代码中每次使用SM服务,都要执行一次打开Binder设备,执行mmap,定会消耗较多系统资源,所以定会有一个封装,即是ProcessState,来专门管理每个app的Binder操作,而且保证,
每一个进程只打开一次binder设备,只做一次内存映射,所有需要binder驱动的线程都共享这一资源;因为每一个线程都有与Binder驱动自由沟通的权利,所以还有一个封装是
IPCThreadState,负责与Binder进行实际的命令通信。实际上,java层并没有直接使用这两个类,而是通过BpBinder间接使用ProcessState,IPCThreadState完成与Binder驱动的通信。
尽管如此,如果app借助于BpBinder,ProcessState和IPCThreadState来跟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会把请求发送给BinderProxy,BinderProxy会通过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 static IBinder getService(String name) {
return getIServiceManager().getService(name);
}
首先要获取ServiceManagerProxy的句柄,通过ServiceManagerProxy获取ServiceManager提供的服务。
private static IServiceManager getIServiceManager() {
IServiceManager sServiceManager;
sServiceManager =
ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
Step1,在进入ServiceManagerNative类之前,先看BinderInternal.getContextObject()的返回值是一个IBinder,实际跟Binder驱动通信的就是这个IBinder,IBinder是一个接口,并且在native层和java层都有定义,分别是:IBinder.h和IBinder.java,这个BinderInternal主要的功能就是提供一个接口,方便获取IBinder对象:
public static final native IBinder getContextObject()@BinderInternal.java;
而且getContextObject是native函数,因为跟Binder驱动通信,最终是要通过JNI调用本地代码来实现。但是ServiceManagerNative.asInterface(…)这里需要的是一个java层的Ibinder对象,所以必然先获取一个native层的Ibinder,然后把native层Ibinder转成java层的Ibinder,来看具体实现:
android_os_BinderInternal_getContextObject()@android_util_Binder.cpp,
这是getContextObject对应的native层实现。
static jobject android_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对象,对应了Ibinder的native层的实现。这里getStrongProxyForHandle()的参数0代表Service manager,handle也就表示了这个proxy是属于谁的,比如0就表示这个proxy属于SM。接下来通过javaObjectForIBinder把BpBinder转成了IBinder的Java层实现,
jobject javaObjectForIBinder(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());
}
这个java层Ibinder实现类是谁呢?就要看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中,所谓的转化就是通过JNI的NewObject创建了一个BinderProxy对象,并且让这个Proxy持有了一个native层Ibinder的引用,也就是这句代码的作用:
env->SetLongField(object,gBinderProxyOffsets.mObject, (jlong)val.get());
这里持有这个引用的一个作用就是做反向转化,即是proxy把请求传给binder驱动时,会再把binderProxy转成BpBinder。
到这里分析完了如何获取Ibinder对象。下面看如何通过Ibinder获取SM的服务。
Step2,getIServiceManager()@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;
}
下面看ServiceManagerProxy对getService的处理:
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;
}
先是Parcel打包准备数据,核心的语句就是mRemote.transact(),最后获取结果,transact之后Binder驱动会把调用者所在进程挂起,直到有了结果在把它唤醒,这个挂起的操作是binder驱动在执行具体命令时通过调用wait_event_interruptible操作的。等服务端处理完成,写入返回数据时通过wake_up_interruptible(&thread->wait);再唤醒被挂起的进程。根据第一步的分析,这个mRemote就是BinderProxy,接着看BinderProxy中transact函数。
final class BinderProxy implements IBinder{
public boolean transact(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的使用过程,不管怎么复杂,最后都还是通过IPCThreadstate和ProcessState来实现的,其中ProcessState负责打开dev/binder/设备节点,执行mmap,IPCThreadstate负责跟Binder驱动做实际的命令交互。
Step3,分析下进程间数据时怎么共享的。
以ServiceManager.getService()这个过程为例,一个是client端进程,一个是server段进程,这里的Server段就是ServiceManager所在进程,一个需要IPC的进程在启动的时候除了打开"/dev/binder",还要做mmap,ServiceManager也是这样的,具体可以看service_manager.c的main函数。
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中指向的物理内存处于同一个位置。
这样Binder驱动和ServiceManager就拥有了若干公用的物理内存块。他们对各自内存的操作,实际是在同一块内存中执行的。
那么现在有一个client端进程想要获取ServiceManager的服务,比如getService(),这个请求在传到binder驱动后,Binder驱动通过copy_from_user把client端进程的某段数据复制到他的bwr->buffer指向的内存空间中,这个copy_from_user的调用是在binder_ioctl()@Binder.c中发生的,这个Binder.c是Binder驱动的文件,具体路径:kernel/drivers/staging/android/Binder.c,要跟ServiceManager中Binder.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间的数据共享。
在执行binder_mmap的过程中,对于同一块物理内存,一方面会映射到进程的虚拟地址空间,另一方面会映射到内核虚拟地址空间,这样进程和内核之间就可以减少一次内存复制的操作,提高了进程间通信的效率。