• 一个Binder的前生今世 (一):Service的创建


    一个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, IInterface, BinderProxy, Binder, BBinder,BpBinder,,Parcel,ProcessState,IPCThreadState 等以及它们的关系。希望您能够通过这系列文章对 binder 的前生今世和生命周期有一个具体的了解和概念。

    Binder 应用及系统层关系图

    先来上一张图:
    在这里插入图片描述
    上图描述了Binder在应用层的架构设计,和与C++层的具体的Binder类的关系以及与系统层(RuntimeLayer, 也可以叫AppLayer,这两个类是在一个application中共享的实例)Binder机制的关系。

    Binder应用层的架构设计

    先来总体介绍下: binder的总体架构主要就是C/S 架构。那么binder的C/S架构是如何设计的呢?我们接着看

    Binder应用层架构设计主要采用了Proxy模式,主要分为三部分:

    1. IBinder接口,主要代表了binder实体的抽象,打个比方就类似于通话人员的对讲机,通讯双方(客户端,服务端)都需要通过这个binder实体来实现通讯。
    2. IInterface接口,主要是提供给定义具体服务接口的IMyService接口来继承的,通过它可以获取binder,也就是拿到对讲机。
    3. IMyService接口, 是一个Proxy模式的实现,具体代表的通讯双方预定好的可以具体提供哪些服务,可以理解为对讲机两端人员的暗号约定,只有约定的暗号,两端的人员才能识别,不然无法识别。它在客户端的代表就是MyService.stub.proxy,在服务端的代表就是MyService.stub.

    当IMyService定义服务接口时,要继承IInterface以便具有可以获取binder实例的能力。

    Binder应用层实现

    好了,理解了上面的binder架构设计,我们接着来分析binder具体是如何实现上诉架构设计的。

    Binder是IBinder在服务端的实现,它实现了onTranscation接口,用来接受客户端发过来的请求, 然后把请求转发给MyService.stub, MyServic.stub就是我们自己实现的处理服务。通常我们在服务端的实现方式为:

    class MyService() : MyService.Stub() 
    
    • 1

    BinderProxy是IBinder在客户端的表示,它被MyService.stub.proxy持有,它实现了IBinder的transact函数,用来转发客户端MyService.stub.proxy的函数调用请求。通常我们在客服端的使用方式为:

    val myService = MyService.Stub.asInterface(service);
    
    • 1

    以上就是Binder在Java应用层的基本实现和原理,就是这么简单,通则不难,理解了这些就可以在我们的应用中使用binder了。

    我们了解了在应用开发中binder是怎么使用的,那么binder的底层的机制是怎样的? 为什么我们要这样用,为什么我们这样用了就可以IPC通讯了呢?

    好,接下来我们就来深入分析binder底层的原理。

    首先我们先来具体看下一个binder,也就是一个binder服务是如何创建出来以及如何与客户端建立起联系的。

    Binder服务端的创建以及与客户端建立联系

    服务端Binder的创建

    先上图:
    在这里插入图片描述

    我们在应用层创建binder的方式一般是在服务端(server)被调用到bind方法的时候,此时,我们会创建并返回一个MyService.stub对象,然后转换成IBinder返回给bind调用。那当我们new一个MyService.stub的时候,系统是如何操作的呢,可以从上图以及开始的类关系图一探流程。

    附上各个类的路径:

    Binder.java : Android/frameworks/base/core/java/android/os/Binder.java
    
    android_util_Binder : Android/frameworks/base/core/jni/android_util_Binder.cpp
    
    JavaBBinderHolder : Android/frameworks/base/core/jni/android_util_Binder.cpp
    
    Parcel : Android/frameworks/native/libs/binder/Parcel.cpp
    
    android_os_Parcel : Android/frameworks/base/core/jni/android_os_Parcel.cpp
    
    JavaBBinder: Android/frameworks/base/core/jni/Android_util_Binder.cpp
    
    BBinder: Android/frameworks/native/include/IBinder.h & Android/frameworks/native/libs/binder/Binder.cpp
    
    BpBinder : Android/frameworks/native/libs/binder/BpBinder.cpp
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    总结: 服务端创建一个service.stub对象,这个对象继承Java层Binder,Java层的Binder在C++层持有一个JavBBinderHolder。然后返回给bind调用,bind调用返回给系统一个IBinder对象,接着系统将这个IBinder对象通过Parcel打包,然后传递此Parcel。在打包Parcel的时候会把Java层的Binder写入Parcel,写入的时候就会调用ibinderForjavaObject来生成JavaBBinder对象,JavaBBinder对象保存对Java层Binder对象的引用在mObject字段中,JavaBBiner对象继承BBinder,进而生成了BBinder对象,BBinder此时就被创建出来了,这个BBinder就是c++层最原本的Binder对象(也就是对讲机)的服务端了。

    服务端Binder的传递

    我们应用层通常都是通过Parcel传递binder的。上一段分析了服务端binder是如何创建的以及都包含了哪些对象,接下来我们再来看下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);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    然后我们再看:

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 相关阅读:
    CentOS安装IRIS
    Django项目实现对外POST接口
    Uniapp小程序 时间段选择限制(开始时间 结束时间相互限制)
    MyBatis Plus详细教程
    如何进行测试分析与设计-HTSM启发式测试策略模型 | 京东云技术团队
    Mybatis中的关系映射
    非关系型数据库技术课程 第五周作业(Redis中的数据持久化、Docker下Redis数据持久化及散列(Hash)数据类型实验)
    如何克服发票自动化的 4 个最常见障碍
    PHP MYSQLi OOP式准备好语句
    Pygame实现黑客帝国屏幕效果
  • 原文地址:https://blog.csdn.net/arrowyi_gbd/article/details/132789169