一个Binder的前生今世 (一):Service的创建
- 一个Binder的前生今世
- Binder的历史 (字面意义的前生今世)
- Binder的生命周期(抽象意义的前生今世)
- Binder 应用及系统层关系图
- Binder应用层的架构设计
- Binder应用层实现
- Binder的创建
- 服务端Binder的创建
- 服务端Binder的传递
- Binder在客户端的传递
- 客户端BinderProxy的创建
- 小结
一个Binder的前生今世
目前介绍binder的文章很多,但是大部分都是分层来介绍的,从驱动再到Service manager等,这些文章对研究binder的机制给了很多的帮助和教学,但是这些大部分是从系统的角度去阐述binder机制的,作为应用开发者,希望能有一个从应用开发角度去理解 binder 和其机制的介绍文章,比如从binder的生命周期角度去了解等,遂产生了这篇文章异或笔记
Binder的历史 (字面意义的前生今世)
binder 的前身是 OpenBinder,它是一种基于对象的分布式组件框架,最早由 BeOS 公司开发,后来被 Palm 公司收购,并用于 Palm OS Cobalt 系统。OpenBinder 采用了一种类似于 COM 或 CORBA 的模型,将跨进程通信抽象为对象之间的方法调用,提供了一套完整的接口和协议来实现对象的创建、引用、继承、代理等功能。
OpenBinder 在 Palm OS Cobalt 系统中并没有得到广泛的应用,而是被 Google 公司收购,并用于 Android 系统中。Google 公司对 OpenBinder 进行了大量的修改和优化,使其更适合移动设备的特点和需求。主要的改变有以下几点:
提供了 Java 语言的绑定。
将 OpenBinder 的对象模型简化为引用计数模型,并去掉了继承、代理等复杂的功能。
将 OpenBinder 的通信协议简化为四种基本类型:数据、命令、句柄和文件描述符,并使用 Parcel 类来打包和解包数据。
将 OpenBinder 的驱动程序从用户空间移动到内核空间,并使用 mmap 和 ioctl 来进行内存映射和控制。
经过这些改变后,OpenBinder 就变成了我们现在所熟知的 binder,它成为了 Android 系统中最重要的跨进程通信机制之一。
Binder的生命周期(抽象意义的前生今世)
binder 的生命周期是指一个 binder 对象从创建到销毁的过程,它涉及到多个进程和线程之间的交互和协作。binder 的生命周期主要包括以下几个阶段:
创建:一个进程或线程可以通过继承 BBinder 类或实现 IBinder 接口来创建一个本地端 binder 对象,并通过注册到 service manager 或写入到 Parcel 中来将其传递给其他进程或线程。
获取:一个进程或线程可以通过查询 service manager 或读取 Parcel 中来获取一个远端 binder 对象的句柄,并通过继承 BpBinder 类或使用 Proxy 类来与之通信。
调用:一个进程或线程可以通过调用 transact 方法来向一个远端 binder 对象发送数据和命令,并等待其返回结果。
响应:一个进程或线程可以通过重写 onTransact 方法来接收并处理来自一个远端 binder 对象的数据和命令,并返回结果。
销毁:一个进程或线程可以通过调用 unlinkToDeath 方法来取消对一个远端 binder 对象的引用,并释放其资源。当一个远端 binder 对象没有任何引用时,它就会被销毁。
在本系列博客中,我会介绍每个阶段的具体实现和源码分析,以及一些相关的概念,例如:IBinder, Interface, BBinder, BpBinder, Parcel、ProcessState、IPCThreadState 等以及它们的关系。希望您能够通过这系列博客,对 binder 的前生今世和生命周期有一个具体的了解。
Binder 应用及系统层关系图
先来上一张图:
上图描述了Binder在应用层的架构设计,和与C++层的具体的Binder类的关系以及与系统层(RuntimeLayer, 也可以叫AppLayer,这两个类是在一个application中共享的实例)Binder机制的关系。
Binder应用层的架构设计
先来总体介绍下: binder的总体架构主要就是C/S 架构。那么binder的C/S架构是如何设计的呢?我们接着看
Binder应用层架构设计主要采用了Proxy模式,主要分为三部分:
- IBinder接口,主要代表了binder实体的抽象,打个比方就类似于通话人员的对讲机,通讯双方(客户端,服务端)都需要通过这个binder实体来实现通讯。
- IInterface接口,主要是提供给定义具体服务接口的IMyService接口来继承的,通过它可以获取binder,也就是拿到对讲机。
- IMyService接口, 是一个Proxy模式的实现,具体代表的通讯双方预定好的可以具体提供哪些服务,可以理解为对讲机两端人员的暗号约定,只有约定的暗号,两端的人员才能识别,不然无法识别。它在客户端的代表就是MyService.stub.proxy, 在服务端的代表就是MyService.stub.
当IMyService定义服务接口时,要继承IInterface以便具有可以获取binder实例的能力。
Binder应用层实现
好了,理解了上面的binder架构设计,我们接着来分析binder具体是如何实现上诉架构设计的。
Binder是IBinder在服务端的实现,它实现了onTranscation接口,用来接受客户端发过来的请求, 然后把请求转发给MyService.stub, MyServic.stub就是我们自己实现的处理服务。通常我们在服务端的实现为:
class MyService() : MyService.Stub()
BinderProxy是IBinder在客户端的表示,它被MyService.stub.proxy持有,它实现了IBinder的transact函数,用来转发客户端MyService.stub.proxy的函数调用请求。通常我们在客服端的使用方式为:
val myService = MyService.Stub.asInterface(service);
以上就是Binder在Java应用层的基本实现和原理,就是这么简单,通则不难,理解了这些就可以在我们的应用中使用binder了。
我们了解了在应用开发中binder是怎么使用的,那么binder的底层的机制是怎样的? 为什么我们要这样用,为什么我们这样用了就可以IPC通讯了呢?
好,接下来我们就来深入分析binder底层的原理。
首先我们先来具体看下一个binder是如何创建出来的。
Binder的创建
服务端Binder的创建
先上图:
我们在应用层创建binder的方式一般是在服务端bind的时候,此时,我们会返回一个MyService.stub对象,然后转换成IBinder返回给binder调用。那当我们new一个MyService.stub的时候,系统是如何操作的呢,可以从上图一探流程。
附上各个类的路径:
Binder.java : Android/frameworks/base/core/java/android/os/Binder.javaandroid_util_Binder : Android/frameworks/base/core/jni/android_util_Binder.cppJavaBBinderHolder : Android/frameworks/base/core/jni/android_util_Binder.cppParcel : Android/frameworks/native/libs/binder/Parcel.cppandroid_os_Parcel : Android/frameworks/base/core/jni/android_os_Parcel.cppJavaBBinder: Android/frameworks/base/core/jni/Android_util_Binder.cppBBinder: Android/frameworks/native/include/IBinder.h & Android/frameworks/native/libs/binder/Binder.cppBpBinder : Android/frameworks/native/libs/binder/BpBinder.cpp
总结: 服务端创建一个service.stub对象,这个对象继承Java层Binder,Java层的Binder在C++层持有一个JavBBinderHolder。
然后在Parcel传递的时候,会把Java层的Binder写入Parcel,写入的时候就会调用ibinderForjavaObject来生成JavaBBinder对象,JavaBBinder对象保存对Java层Binder对象的引用在mObject字段中,JavaBBiner对象继承BBinder,进而生成了BBinder对象,BBinder此时就被创建出来了,这个BBinder就是最根本的Binder对讲机的服务端了。
服务端Binder的传递
我们应用层一般时候binder都是通过Parcel传递的。我们接下来就来看看,我们服务端生成了一个Service的binder后,是如何通过Parcel传递出去的。
首次第一步肯定是先把binder写入Parcel ,我们先来分析Java层,首先肯定是调用Java层的Parcel 写入binder,进而就会调用到Parcel的Jni层执行写入操作:
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);if (parcel != NULL) {const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));if (err != NO_ERROR) {signalExceptionForError(env, clazz, err);}}
}
然后我们再看:
sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{if (obj == NULL) return NULL;// Instance of Binder?if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {JavaBBinderHolder* jbh = (JavaBBinderHolder*)env->GetLongField(obj, gBinderOffsets.mObject);return jbh->get(env, obj);}// Instance of BinderProxy?if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {return getBPNativeData(env, obj)->mObject;}ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);return NULL;
}
到这里就会调用到JavaBBinderHolder的get函数,对应到我们服务端Binder的创建流程,这里就会生成JavaBBinder了:
class JavaBBinderHolder
{
public