Android源码分析:Binder概述与ServiceManager获取
阅读Android系统源码,Binder是绕不过去的东西,前面看ContentProvider,Activity都有Binder的身影,因此决定还是先把Binder的部分看一看。本文主要简单介绍一下Binder的历史和它的基本架构,介绍Binder的ServiceManager我们在使用的时候如何去拿到它,同时推荐一些Binder的学习资料。 Binder简介 对于普通的Android的应用开发来说,进程的概念是被弱化的。这得益于系统已经帮助我们把Activity,ContentProvider,Broadcast,Service等涉及到跨进程的组件做了很好的封装。我们知道Android也是基于Linux进行开发的,那比如存在跨进程,也就必然存在跨进程通讯。Linux当中跨进程通讯常常使用共享内存、信号量、管道等方式,不过Android中为了安全和使用的便利性,则大部分地方都是使用了Binder。 Binder并不是新提出来的一套跨进程通信机制,它是基于OpenBinder实现的。Binder最早是Be公司开发的, George Hoffman需要一种机制让Be的互联网设备的Javascript UI层与地秤系统服务发生交互,边开发了Binder。后来Be公司的工程师加入了PalmSource开发Palm OS,再后来加入Android,Binder也一直被他们采用,并且也在一直演化,对这段历史感兴趣的话,可以看看《安卓传奇:Android缔造团队回忆录》这本书。开源的OpenBinder是可以工作在Linux内核中的,在2015年已经被合并到Linux 内核3.19版本当中,不仅仅Android,华为的鸿蒙系统当中也在使用Binder。 Binder基本架构 Android中的Binder包括Binder驱动,ServiceManager,Binder服务,他们的关系大概如上图所示。Binder驱动位于Linux内核层,它主要用于实现多个进程之间的通信。ServiceManager位于Android Framework层,用于Binder服务的注册和查找,相当于网络服务中的DNS。而Binder服务的Server端和Client端就是典型的C/S架构,它们通过Binder驱动来进行交互。Android中有两种Binder服务,一种是类似于AMS,PMS这种的系统服务,它们是有名称的服务,注册在ServiceManager当中,Client可以通过名称查找到他们进行使用。还存在另一种匿名Binder服务,比如我们自己通过AIDL创建的,这种我们会直接通过其他的Binder服务把Binder引用传递到客户端,从而双方可以进行通讯。 Binder驱动的源码是在Linux当中的,暂时先不关注了。我这里主要会去看Android Framework层当中Binder相关的代码。ServiceManager本身也是一个Binder,它的ID为0,因此可以很简单的拿到,它的初始化我就不关注了。首先会去关注我们在应用层如何去拿到ServiceManager,因为只有拿到它才能够使用它去注册Binder和获取Binder。其实我们再去看看ServiceManger的addService如何注册一个Binder服务,以及getService 如何获取一个Binder服务。这些看完之后,我们也就知道了Binder的完整运行过程,因为addService和getService本身也是binder调用,其中我们也会分析Framwork调用kernel相关的代码。最后我们再看看匿名Binder,AIDL,这样差不多就可以对于Framework层的Binder有了全面的了解。 获取ServiceManager ServiceManager本身是存在一个单独的进程的,并且是在系统启动的时候就启动了它。而我们在其他进程想要通过它来注册服务或者获取服务,就需要首先拿到它的Ibinder对象。通常会用如下的方式获取: 1 2 3 4 5 6 7 8 9 10 private static IServiceManager getIServiceManager() { if (sServiceManager != null) { return sServiceManager; } // Find the service manager sServiceManager = ServiceManagerNative .asInterface(Binder.allowBlocking(BinderInternal.getContextObject())); return sServiceManager; } 上面的代码首先会拿本地的缓存,拿不到才会真正调用获取ServiceManager的步骤,我们先看看这个BinderInternal.getContextObject()方法,它是一个native方法,它的实现在base/core/jni/android_util_Binder.cpp当中,代码如下: 1 2 3 4 5 static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz) { sp<IBinder> b = ProcessState::self()->getContextObject(NULL); return javaObjectForIBinder(env, b); } 首先调用ProcessState::self()来拿到ProcessState实例,它内部会执行如下代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault) { if (driver == nullptr) { std::lock_guard<std::mutex> l(gProcessMutex); if (gProcess) { verifyNotForked(gProcess->mForked); } return gProcess; } [[clang::no_destroy]] static std::once_flag gProcessOnce; std::call_once(gProcessOnce, [&](){ if (access(driver, R_OK) == -1) { driver = "/dev/binder"; } int ret = pthread_atfork(ProcessState::onFork, ProcessState::parentPostFork, ProcessState::childPostFork); //注册fork进程的监听 LOG_ALWAYS_FATAL_IF(ret != 0, "pthread_atfork error %s", strerror(ret)); std::lock_guard<std::mutex> l(gProcessMutex); gProcess = sp<ProcessState>::make(driver); //智能指针初始化 }); if (requireDefault) { ... } verifyNotForked(gProcess->mForked); return gProcess; } 上面的代码传入的参数driver值为/dev/binder也就是binder驱动的地址,requireDefault为false。上面的代码中的std:call_once方法为android的libc所提供,就是保证下面的代码段只会执行一次,这个实现也就是为了实现单例,和Java代码中的其实是差不多的。代码中的make方法内部实际会调用ProcessState的构造方法,代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 ProcessState::ProcessState(const char* driver) : mDriverName(String8(driver)), mDriverFD(-1), mVMStart(MAP_FAILED), mThreadCountLock(PTHREAD_MUTEX_INITIALIZER), mThreadCountDecrement(PTHREAD_COND_INITIALIZER), mExecutingThreadsCount(0), mWaitingForThreads(0), mMaxThreads(DEFAULT_MAX_BINDER_THREADS), mStarvationStartTimeMs(0), mForked(false), mThreadPoolStarted(false), mThreadPoolSeq(1), mCallRestriction(CallRestriction::NONE) { base::Result<int> opened = open_driver(driver); if (opened.ok()) { mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, opened.value(), 0); if (mVMStart == MAP_FAILED) { close(opened.value()); //mmap失败,关闭binder文件描述符 opened = base::Error() << "Using " << driver << " failed: unable to mmap transaction memory."; mDriverName.clear(); } } verifyNotForked(gProcess->mForked); //检查当前的实例不是fork之后的只进程的实例否则报错 if (opened.ok()) { mDriverFD = opened.value(); //记录binder的文件描述符 } } open_driver内部就是调用linux的系统调用open打开binder驱动,并通过ioctl获取驱动打开状态以及进行驱动的一些设置如最大线程数等,这里就查看相关代码了。 打开驱动后又会调用mmap把进行内存映射并保存内存指针到mVMStart上,其中内存映射的大小为BINDER_VM_SIZE,定义如下: 1 2 #define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2) #define _SC_PAGE_SIZE 0x0028 其中sysconf(_SC_PAGE_SIZE)之前的值为4k,最新的Android 15改成了16K,那我们这里仍然以来版本计算,可以得到值为1016Kb,这也就是我们使用Binder交互时候数据传输的限制。 这里我们拿到了binder的文件描述符,也完成了内存映射,也就完成了ProcessState的初始化。ProcessState这个对象如它的名字,在每个进程当中只会有一个实例。 有了实例我们又可以继续看getContextObject,主要代码如下: 1 2 3 4 5 6 7 8 9 sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*calle
以上内容来自博客订阅源,版权归原作者。关注作者后可以在 关注列表 第一时间查看作者文章。

登录后,加入这场讨论。
登录参与