美文网首页
聊一聊Binder

聊一聊Binder

作者: 李海洲 | 来源:发表于2017-05-26 23:56 被阅读0次

    Binder对于理解Android系统至关重要, 本文不会从代码细节入手,而是宏观上对binder机制进行介绍。旨在通过本文能够对binder有一个整体上的认识,为后面深挖细节做好铺垫

    Binder架构
    Binder通信的参与者有如下四部分
    1.Binder驱动:Binder的核心,实现Binder的各种底层操作
    2.ServiceManager:提供Binder的名称到引用对象的转换服务
    3.服务端:Binder服务提供者
    4.客户端:Binder服务使用者

    图片.png

    这个图想必各位都很熟悉了,很多介绍Binder的博文都会出现这个图的身影

    如上简介,对Binder机制先有一个感性的认识

    Part1:Binder对象定义
    1.Binder实体对象:Binder实体对象就是Binder服务的提供者,一个提供Binder服务的类必须继承BBinder类,有时候为了强调对象的类型,也用"BBinder对象"来代替"Binder实体对象"
    2.Binder引用对象:Binder引用对象是Binder实体对象在客户进程的代表,每个引用对象的类型都是BpBinder类,同样可以用名称"BpBinder对象"来代替"Binder引用对象"
    3.Binder代理对象:代理对象也称为接口对象,主要是为客户端的上层应用提供接口服务,从IInterface类派生。通过代理对象,应用能够像使用本地对象一样使用远端的实体对象提供的服务
    4.IBinder对象:BBinder和BpBinder类都是从IBinder类中继承而来。在很多场合,如果不需要刻意去区分实体对象和引用对象,则可以使用"IBinder对象"统一称呼它们

    Part2:Binder服务的类别
    实名服务:
    Android的实名Binder服务都是系统提供的,如AMS,PMS,WMS等,实名服务可以通过ServiceManager查询到
    匿名服务:
    普通应用开发的Binder服务,鉴于这种服务不能通过ServiceManager查询到,使用者又是如何得到该服务的引用对象呢?
    Android通过framework提供了一种启动java匿名Binder服务的方法,首先某个应用通过bindService()方法发出一个Intent,Framework根据Intent找到对应的组件Service并启动它,包在组件Service中的Binder服务也将同时创建出来。随后framework会把服务的Ibinder对象通过ConnectivityManager的回调方法onServiceConnected()传回到应用,这样应用就得到匿名Binder服务的引用对象,也就能使用组件Service中的匿名Binder服务了,在这里Android的framework使用intent代替了Binder服务的名称来查找对应的服务,同时也承担了ServiceManager的工作,解析intent并传回服务的引用对象

    图片.png

    Part3:Binder机制的角色
    Binder机制包括四个角色:Binder driver、Service Manager、Service、Client.
    Binder driver
    Binder driver工作于内核态(kernel space), 作为内核的一部分,跟随linux系统启动。它向linux内核注册了MISC设备,即dev/binder设备文件
    Binder驱动位于Binder架构的核心,通过文件系统的标准接口,如open()、ioctl()、mmap()等,向用户层提供服务。应用层和Binder驱动之间的
    数据交换是通过ioctl()完成的
    简而言之,Binder的主要功能是提供Binder通信的通道,维护Binder对象的引用计数,转换传输中的BInder实体对象和引用对象以及管理数据缓存区

    Service Manager
    Service Manager作为所有实名Binder的管理者,管理着系统中常用的基本Service(这里的Service与Android四大组建之一的Service是不同的概念),例如MediaPlayerService、CameraService、BlueToothService等。
    首先,所有这些Service启动后,会把自己注册到Service Manager中。然后,Service Manager就把Service的handle添加到内部的列表中。最后,Client向Service Manager索取Service的Handle时,Service Manager就从内部的列表中查找对应service的handle,并返回给Client,之后Client就可以根据Handle向Service申请自己需要的服务。
    从广义角度来说,拥有Binder实体的进程即是Service,从这个角度来看,ServiceManager本身也是一个Service,同时它也是系统内第一个启动的Service。从功能的角度来说,Service Manager相当于一个全局列表,Service把自己添加到列表中,所以Client可以从列表中检索自己需要的Service。

    Service
    Service即服务的提供者,每个Service都拥有一个Binder实体,Service可以根据需要把自己注册到Service Manager(即实名Binder),也可以不注册(即匿名Binder)。匿名Binder必须依赖实名Binder才能工作(因为它必须通过一个Binder把自己发送到Client端,才能开始Binder通信)

    Client:
    Client即服务的使用者,Client持有一个Binder引用,而Binder引用则指向特定的Binder实体。Client通过这个Binder引用与Binder实体(即Service)通信,从而获取Service的服务。广义上来说,持有Binder引用即为Binder机制的Client,除了ServiceManager这个特例,ServicerManager持有所有实名Binder的引用,但是从来不呼叫这个Binder的服务。

    Part4:Binder层次

    图片.png

    驱动适配层
    主要是IPCThreadState.cpp和ProcessState.cpp,源码位于frameworks/native/libs/binder
    这两个类都采用了单例模式,主要负责和驱动直接交互。
    ProcessState负责打开binder设备,进行一些初始化设置并做内存映射
    IPCThreadState负责直接和binder设备通信,使用ioctl读写binder驱动数据

    Binder核心框架层
    Binder核心框架主要是IBinder及它的两个子类,即BBinder和BpBinder,分别代表了最基本的服务端及客户端。
    binder service服务端实体类会继承BnInterface,而BnInterface会继承自BBinder,服务端可将BBinder对象注册到servicemananger进程。
    客户端程序和驱动交互时只能得到远程对象的句柄handle,它可以调用调用ProcessState的getStrongProxyForHandle函数,利用句柄handle建立BpBinder对象,然后将它转为IBinder指针返回给调用者。这样客户端每次调用IBinder指针的transact方法,其实是执行BpBinder的transact方法。

    Part5:使用Binder服务
    该部分将讲述如何使用已有的Binder服务以及如何开发Binder服务

    C++层和Java层使用Binder服务的方式基本一样,包括函数的接口类型都相同,这里就不区分java和C++了
    需要如何操作才能获取到Binder服务引用对象呢?
    先得到ServiceManager服务的引用对象,再根据ServiceManager的getService方法来查找注册的Binder服务,如果getService找到名称对应的服务,它会返回服务的IBinder对象,否则为null。注意这里得到的IBinder对象是引用对象,我们都知道应用层实际需要使用的是代理对象,所以在使用前需要将引用对象转换为代理对象,转换方式举例如下:
    ICameraService service = ICameraService.Stub.asInterface(binder);
    这里的这个asInterface作用是通过引用对象这个参数来新创建一个Binder代理对象,代理对象中包含了引用对象

    Part6:用Java开发Binder服务
    Binder使用简单,开发一个Binder服务就稍微复杂一些,不过相对于使用C++开发Binder还是简单得多
    Step1:
    编写一个AIDL文件,定义两个简单的方法get()和set(),如下所示
    interface IExempleService{
    int get();
    void set(int value);
    }
    AIDL是用来简化Binder编程的脚本语言,只需要在AIDL文件中定义出方法接口,AIDL解释器能自动生成服务端和客户端所需要的java代码,这也是java服务比C++简单的多

    Step2:编写Service的代码

    图片.png

    ExampleService需要从Service类继承,而且必须重载底层的onbind()方法,并在其中返回Binder服务的实体对象mInstance。
    IExampleService.stub类是真正的Binder服务类,它是通过前面的AIDL文件自动生成的,因此还需要继承IExampleService.stub重载它的get和set方法,并实现它们的功能

    Step3:
    在AndroidManifest.xml中加入服务的声明

    图片.png

    这里加入声明的作用是让framework能够通过intent找到Service

    Part7:Binder实现原理
    一般的对象作为参数传递没有太大的问题,只需要实现序列化和反序列化就能实现。但是,当Binder对象作为参数传递时,就会面临实体对象和引用对象相互转换的问题,为了能够让上层使用方便,这个转换过程在驱动中自动完成

    普通的IPC传递参数数据时,要经历两次数据复制的过程,一次从调用者的数据缓冲区复制到内核的缓冲区,第二次从内核的缓冲区复制到接收进程的读缓冲区中。为了提高效率,Binder为每个进程创建了一块缓存区,这块缓存区在内核和用户空间共享,传输数据只需要从发送进程的用户空间的缓冲区复制到目标进程在驱动的缓冲区,但是目标进程从驱动中读取数据就不需要从内核空间复制到用户空间了,而是直接从和内核共享的缓存区读取,这样就减少了一次数据复制全局的过程

    最后考虑服务进程中BInder调用的执行,每次执行必须在一个线程中完成,如果线程不停地创建和释放,势必会带来很大的系统开销,这样利用线程池来管理Binder调用的执行线程就顺理成章了。在Binder的设计中,除了第一个线程是应用层主动创建的,线程池中的其它线程都是在驱动的请求下才创建的,这样不但将线程的使用数量降到最低,而且能够保证从驱动到来的Binder调用有线程刻意使用

    最后,贴一张网上找的图,个人觉得不错

    小结
    对Binder调用的过程如下:
    1.客户端从某个线程中发起调用,将参数打包后通过ioctl()函数传递给驱动
    2.客户端挂起并等待ioctl()返回函数的结果
    3.驱动记录下调用进程的信息,然后根据调用的Binder对象信息寻找服务所在的进程
    4.驱动找到服务进程后先查看进程中是否有空闲进程,没有则通知服务进程创建
    5.得到空闲进程后根据驱动中保存的BBinder对象的指针调用相应的函数
    6.函数返回后再通过ioctl()把结果打包传递给驱动
    7.驱动根据返回信息查找调用者线程
    8.找到调用者线程后唤醒它,并通过ioctl()函数把结果传递回去
    9.客户端的线程得到返回结果后继续运行

    本文内容整理自《深入解析Android5.0系统》一书,基本都是手动打字上去的,BInder代码层的分析工作是非常庞大而且艰辛,后面会更新代码层的分析文章

    相关文章

      网友评论

          本文标题:聊一聊Binder

          本文链接:https://www.haomeiwen.com/subject/kgoxfxtx.html