Android Binder小结

Posted guangdeshishe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Binder小结相关的知识,希望对你有一定的参考价值。

Binder小结

什么是Binder

  • Binder是android中一种跨进程通信方式,Binder也是一个虚拟设备,对于客户端来说Bidner是一个可以跨进程通信的一个类

为什么Android要使用Binder进程间通信?

  • Android底层是Linux,但是Linux已有的跨进程通信方式都不能满足Android移动设备的需求,在Android中跨进程通信方式,要求是CS的一对多的架构、需要保证安全,并且效率高;在传统Linux跨进程通信方式中,Socket是CS架构,但是它不够安全、而且需要两次从用户空间和内核空间之间的拷贝;管道它比较安全,但是它只适合1对1,并且也需要两次拷贝;共享内存效率很高,1次拷贝都不需要,支持多对多,但是它不安全;还有其他像信号量、文件、消息等通信方式都无法完全满足Android移动设备的需求,所以Google工程师就开发了Binder作为Android中主要的通信方式,它属于CS架构,支持1对多,在安全性上通过uid、pid进行鉴权,效率上只需要1次拷贝就可以了

Binder实现原理

Binder模型主要包含这么几个角色:客户端Client、服务端Service、ServiceManager、Binder驱动;

  • Binder驱动是实现进程间通信的核心,它的核心原理在于使用了mmap内存映射技术,在内核空间中,将内核数据缓冲区的一块内存和内核中数据接收缓冲区进行了内存映射,然后数据接收缓冲区又跟用户空间中接收数据方所在进程有内存映射关系,所以当数据发送方所在进程发送数据时,会先将数据序列化后拷贝到内核数据缓冲区,由于内核数据缓冲区和数据接收缓冲区以及接收方用户进程内存映射,所以相当于直接将数据发送到了数据接收方所在进程;它之所以高效的原因就是因为使用了内存映射技术,减少了从内核空间拷贝到数据接收方所在的进程的过程

  • ServiceManager主要用于管理AMS\\PMS等系统服务对应的Binder;当客户端需要使用系统服务的时,通过ServiceManager拿到对应系统服务的Binder代理类,比如ActivityManager\\WIndowManager;这些代理类内部会持有系统服务器对应的Binder,通过跨进程方式实现方法的调用;

    • ServiceManager是在系统启动时init进程中启动的,同时启动的还有Zygote进程,Zygote进程又会创建SystemServer进程,SystemServer进程又会去启动AMS、PMS这些系统服务,然后这些系统服务又会以Binder形式注册到ServiceManager中,ServiceManager会在Binder驱动红黑树中添加相应的节点,保存相应的信息

    • ServiceManager在native层启动时,会打开Binder驱动,将自身作为服务注册到Binder句柄中(句柄号为0),然后开启binder循环接收客户端发过来的请求,处理系统服务器的注册、查找等

  • 为了简化客户端和服务端之间使用Binder跨进程通信的过程,Android提供了AIDL(全称是Android Interface define language);

    • 在aidl文件中定义服务端要提供的方法,编译时会使用aidl命令将我们定义的接口生成一个java接口类;这个接口类继承自IInterface接口;里面主要有两个内部类Stub和Proxy代理类;

    • 其中Stub类是继承自Binder并实现了我们定义的接口;其中有个onTransact方法以及用户定义的方法,在onTransact这个方法中会根据传递进来的参数,决定调用服务端哪个方法,并将结果进行Pacel序列化后返回,这个Stub类需要服务端在onBind方法中实现并作为Binder返回给客户端

    • 另一个是Proxy代理类,当客户端拿到服务端返回的Binder对象时,需要将改Binder对象转换成这个Proxy代理对象,这个代理类实现了用户定义的接口,并代理服务端的Binder对象进行远程调用,当客户端通过这个代理对象调用服务端某个方法时,会将要调用的方法名、方法参数序列化后,通过Binder的transact方法发送出去

    • 接着底层会通过BpBinder将数据转发到内核层的Bidner驱动,再通过内存映射直接发送到服务端所在进程,接着BbBinder接收到数据后会调用服务端Stub类里的onTransact方法,服务端这边再根据参数调用相应的方法并返回结果

Bundle传递数据为什么需要序列化?

因为Bundle传递数据,大多数是用于跨进程通信,进程之间内存空间是相互隔离的,无法直接访问,只有通过序列化之后,通过从用户空间拷贝到系统内核,再从系统内核拷贝到用户空间,才可以在另一个进程中通过反序列化后接收到

Binder线程池

在进程创建的时候,ProcessState里会为当前进程打开Binder驱动,通过mmap创建Binder数据接收缓存区,以及创建Binder线程池并开启主线程,默认最大是15个,Binder线程池中线程分为3类:主线程、普通线程、将当前线程加入到线程池的线程;

  • App中有多少Binder线程,是固定的吗?

    • app启动时在创建进程时默认会创建一个Binder主线程在运行,如果App中定义个其他服务在独立进程中,每个进程都至少会启动一个Binder主线程,后面根据跨进程通信请求次数,Binder会自动调成线程个数

    • 最大的Binder线程也不是固定的,在ProcessState类中定义的默认最大线程个数是15个,这15个(不包括主Binder线程和将当前线程加入到Binder线程池中的),这个最大线程个数是可以修改的,比如SystemServer中就将线程池线程最大个数改成了31个

用户空间和内核空间区别?

用户空间是指app代码运行所在空间,内核空间是指系统代码、驱动、内核等运行所在的空间,之所以要划分用户空间和内核空间,是为了让app的代码和系统代码互相隔离开来,如果说没有划分,那么app代码和系统代码就都在一个进程里了,当app崩溃了,系统也会跟着奔溃,而且不同app进程之间也没办法公用系统代码,划分了之后,系统代码在内核空间,可以跟所有app公用,并且app崩溃不会导致内核崩溃,保证系统的安全

什么是物理地址和虚拟地址?

物理地址就是内存条的真实地址,虚拟地址是MMU内存管理单元出来之后才有的,虚拟地址是给cpu用的,cpu不能通过虚拟地址直接访问内存,需要通过MMU转换后才能访问到真实的物理内存;

最早期的计算机cpu是可以直接访问内存真实物理地址的,当时的软件都很小,可能只有几kb大小,可以将软件直接加载进内存运行,但是后面软件越做越大,如果直接全部代码加载到内存就会导致内存很快不够用,为了解决这个问题就引入了MMU对物理内存进行管理,CPU不能直接访问物理内存,需要访问物理内存时需要经过MMU将虚拟地址转换成物理地址才行;执行的代码也只加载当前活跃的代码到内存中,执行完后再从磁盘告诉缓冲中读取,

Binder最大传输数据是多少?为什么?

  • 对于普通app来说是1M-8k,因为在创建进程的时候,ProcessState会在app用户空间和内核之间会通过mmap开辟一块空间进行内存映射,这块空间的大小就是1M-8k,进程之间传递数据都是通过这块内存来完成的,所以普通app最大限制是1M-8k

  • 但是我们也可以在jni开发中手动打开binder驱动,然后调用mmap方法定义一个超过1M-8k大小的内存映射空间用于进程之间传递数据,但是也不能是无限大的,因为在mmap方法中限制最大只能开辟4M的内存映射空间大小;而且不建议这么做,因为Binder设计的最初目的并不是为了跨进程传递大数据,而只是用于通信用的,如果要传递大数据,完全可以通过文件、Socket、共享内存等其他方式传递;如果强制重新开启Binder可能会对已有Binder驱动造成影响,从而影响app正常与系统服务的交互

  • 之所以是1M-8K而不是1M或者其他,是因为MMU内存管理单元从磁盘加载活跃代码时,为了避免频繁的IO,所以规定每次从磁盘至少读取1页的数据,1页就是4k数据大小,所以大小指定是4k的整数倍

  • 实际使用过程中往往可传递的数据小于1M-8k,这是因为Binder线程中可能有多个线程(最大15个)正在跨进程通信,多个线程之间是共用这1M-8k大小的;而且在跨进程过程中,往往除了用户要传递的数据,还需要携带包括进程信息、目标进程信息、要调用的方法等等信息在里面,所以实际可能的空间往往更小

简单讲讲mmap原理?Binder如何做到1次拷贝?

Binder会在内核空间开辟两块内存,一块用于接收从发送方进程传递过来的数据,一块用于与接收方进程通过mmap进行内存映射,这几块内存空间之间都相互映射,当数据通过copy_from_user拷贝到内核缓冲区时,因为内存映射关系,接收方进程可以直接读取到这部分数据,从而省去了copy_to_user将数据从内核拷贝到接收方进程这一步,从而实现1次拷贝

内存中的1页是什么,你怎么理解的?

1页指的是硬盘数据加载到内存时,一次读取的数据数量是4k,这是为了避免频繁的进行io导致的性能问题

AIDL生成的java类结构?

生成的接口类包含了我们在aidl中定义的方法,继承自IInterface,里面有两个内部类分别是Proxy和Stub,他们都实现了我们定义的接口;

  • 其中Stub类继承自Binder,内部有个onTransact方法,当跨进程远程调用服务端方法的时候,这个方法会被调用,在这个方法里会判断要请求的是哪个方法,然后调用服务端中这个Stub类的实现类中的方法;并将要返回的结果写入Parcel对象中

  • 而Proxy代理类是用于代理Binder对象进行跨进程访问,当调用代理类的方法时,内部会先将要请求的参数写入Parcel对象中,然后调用Binder的transact方法进行远程调用并返回结果

  • 当调用Binder的transact方法后,底层会通过BpBinder封装数据后转发远程调用请求,然后将数据拷贝到内核层,通过Binder驱动转发给服务端,服务端进程那边会通过BbBinder对数据进行解析,最终调用服务端Stub实现类里的onTransact方法

BindService启动Service与Binder服务实体的流程?

  • 客户端调用bindService方法后,会跨进程调用AMS方法去查找要启动服务的信息,判断对应的服务所在进程是否已经启动,如果还没有则先通知Zygote启动进程

  • 进程启动后会检查对应的Service是否已经创建,如果没有的话会通知ActivityThread先创建服务,创建完了之后会调用它的生命周期方法onCreate和onBind,在onBind方法中服务端会实现Stub类,这个Stub类继承自Binder

  • 然后Service所在进程会将Binder对象返回给AMS,ANS则会回调客户端的ServiceConnection接口的onServiceConnected方法,并把服务端onBind方法返回的Binder对象返回给客户端

  • 客户端拿到Binder对象后调用asInterface方法,这个方法里会根据是否跟服务端在一个进程中,来决定是返回服务端接口本身,还是返回支持跨进程通信的代理类Proxy

  • 接着客户端就可以直接调用Service提供的方法了

Binder如何找到目标进程的?

binder服务会事先在Binder驱动的红黑树中注册结点,当跨进程通信时Binder驱动会先从红黑树中查找目标Binder所在结点,从结点中获取进程、线程信息,然后去唤醒目标进程、线程

Binder严格意义上拷贝几次?

拷贝两次,在从用户空间拷贝到内核空间的时候,调用了两次copy_from_user方法,一次用于拷贝传递的方法和参数等信息、另一次拷贝进程、线程等附加信息;之所以两次而不是一次,可能是考虑到如果第一次拷贝失败时,就可以直接return,而不用进行第二次拷贝了,节约了拷贝的时间

Binder中的红黑树,为什么会有两棵binder_ref红黑树

一个是用于通过句柄查找,一个是用于通过结点查找,提高查找效率

系统服务与bindService等启动的服务的区别

系统服务的注册、和查找是通过ServiceManager完成的,而bindService则主要是通过AMS来完成的

Binder线程、Binder主线程、Client请求线程的概念与区别

在进程创建的时候,process_state类中会创建Binder线程池,创建Binder线程池所在线程叫做Binder主线程,它会监听客户端发过来的Binder的请求,然后为会客户端分配Binder线程,Binder线程是通过线程池创建的;客户端请求线程就是客户端发起Binder请求所在线程

为什么内核有内核缓存区还要有个数据接收缓存区?

因为内核缓冲区是一直就存在的,以往的跨进程通信就是先从发送方拷贝到内核缓存区,然后再拷贝到接收方,所以需要两次拷贝,Binder的出现创建了一块数据接收缓存区,通过mmap将接收方和系统内核缓存区连接起来,从而减少了一次拷贝

Android 进阶——Framework 核心之Binder 对象及其生命周期小结

文章大纲

引言

前面几篇文章都数次提到了Binder ,那么Binder 到底是什么呢?

一、Binder概述

Android 中核心IPC 机制就是通过Binder实现的,Binder 翻译过来是粘合剂的意思,Android 在架构上一直希望模糊进程的概念,而是以组件替代。应用不需要关心组件存放的位置、运行组件的进程和组件的生命周期等细节,却可以在系统中随时随地地进行进程间通信,这就需要通过一种手段把组件“粘合”起来,于是乎Binder应运而生,与传统IPC 相比,融合了远程调用RPC概念,一种面向对象而非面向过程的RPC调用,Binder就像一张网跨越进程和线程将系统组件组织在一起,只要拥有Binder对象就可以使用其组件的功能,即两个进程之间通过Binder对象互相通信,Android Binder IPC架构以面向对象的调用方式使用Binder,使得在调用远程对象时和调用一个本地对象实例一样。

此组件非Android 四大组件中的组件。

二、Binder 对象

一系列Binder实体对象(binder_node)和Binder引用对象(binder_ref)。在用户空间,运行在Server端的称为Binder本地对象,运行在Client端的称为Binder代理对象;而在内核空间,Binder实体对象用来描述Binder本地对象Binder引用对象用来描述Binder代理对象。

图摘自https://blog.csdn.net/universus/article/details/6211589?spm=1001.2014.3001.5501

三、Binder 对象生命周期的管理

从上面Binder IPC交互过程可以看到,Binder代理对象与Binder引用对象存在映射关系,Bin der引用对象又与Binder实体对象存在多对一的映射关系,Binder实体对象还依赖于Binder本地对象,为了维护管理这些Binder对象,Binder 采用了引用计数来维护每一个Binder对象的生命周期。

如上图所示,假如Client进程将一个Binder代理对象封装成三个不同的BpInterface对象且三个BpInterface对象都通过强指针引用该Binder代理对象(即该代理对象的强、弱引用计数都等于3)。而一个Binder代理对象只有在创建时才会增加相应的Binder引用对象的弱引用计数,且在第一次被强指针引用时才会增加相应的Binder引用的强引用计数,因此Binder引用对象的强、弱引用计数都为1,小于引用了它的Binder代理对象的强、弱引用计数,即一个Binder代理对象的引用计数与其的引用对象的引用计数是N:1的关系。这样就可以减少Client与Binder驱动的交互,较少BC_ACQUIRE、BC_INCREASE、BC_RELEASE和BC_DECREFS协议交互。

1、Binder本地对象(BBinder)的生命周期管理

Binder本地对象类型为BBinder,由用户空间的Server进程创建,一方面会被Server进程的其他对象引用(得益于Android 智能指针技术(RefBase),Server进程其他对象可以通过智能指针来引用这些Binder本地对象。),另一方面被Binder驱动的Binder实体对象间接引用。因为Binder 驱动的Binder实体对象运行于内核空间,不能直接通过智能指针来引用,需要Binder驱动和Server进程约定一套协议来维护它们的引用计数,随即引入了Server Manager 角色参与管理。

Server进程将一个Binder本地对象注册到Server Manager时,Binder驱动就会自动为它创建一个Binder实体对象。当Client进程通过Server Manager来查询一个Binder本地对象的代理对象时,Binder驱动就为Binder代理对象所对应的Binder实体对象创建一个Binder引用对象,然后通过BR_INCREFS和BR_ACQUIRE协议来通知相应的Server进程增加对应的Binder本地对象的弱引用计数和强引用计数的值。这样就能确保Client进程中的Binder代理对象在引用一个Binder本地对象期间,Binder本地对象不会被销毁;而当没有任何Binder代理对象引用一个Binder本地对象时,Binder驱动就会使用BR_DECREFS和BR_RELEASE协议通知Server 进程减烧对应的本地对象的弱引用和强引用计数的值。

Binder驱动通过BR_INCREFS、BR_ACQUIRE、BR_DECREFS和BR_RELEASE协议来引用运行在Server进程的Binder本地对象并协助管理引用计数的值。

\\kernel\\drivers\\android\\binder.c

static int binder_thread_read(struct binder_proc *proc,struct binder_thread *thread,binder_uintptr_t binder_buffer, size_t size,binder_size_t *consumed, int non_block)

    ...
    while (1) 
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w = NULL;
            ...
        if (!binder_worklist_empty_ilocked(&thread->todo))
            list = &thread->todo;
        else if (!binder_worklist_empty_ilocked(&proc->todo) &&
               wait_for_proc_work)
            list = &proc->todo;
        else 
            binder_inner_proc_unlock(proc);
            /* no data added */
        
            ...
        switch (w->type) 
        ...
        case BINDER_WORK_NODE: 
            struct binder_node *node = container_of(w, struct binder_node, work);
            int strong, weak;
            ...
         //检查改Binder实体对象是否有强引用计数和弱引用计数
            strong = node->internal_strong_refs ||
                    node->local_strong_refs;
            weak = !hlist_empty(&node->refs) ||
                    node->local_weak_refs ||
                    node->tmp_refs || strong;

            if (weak && !has_weak_ref) 
                node->has_weak_ref = 1;
                node->pending_weak_ref = 1;
                node->local_weak_refs++;
            
            if (strong && !has_strong_ref) 
                node->has_strong_ref = 1;
                node->pending_strong_ref = 1;
                node->local_strong_refs++;
            
            if (!strong && has_strong_ref)
                node->has_strong_ref = 0;
            if (!weak && has_weak_ref)
                node->has_weak_ref = 0;
            ...
            if (weak && !has_weak_ref)
                ret = binder_put_node_cmd(
                        proc, thread, &ptr, node_ptr,
                        node_cookie, node_debug_id,
                        BR_INCREFS, "BR_INCREFS");
            if (!ret && strong && !has_strong_ref)
                ret = binder_put_node_cmd(
                        proc, thread, &ptr, node_ptr,
                        node_cookie, node_debug_id,
                        BR_ACQUIRE, "BR_ACQUIRE");
            if (!ret && !strong && has_strong_ref)
                ret = binder_put_node_cmd(
                        proc, thread, &ptr, node_ptr,
                        node_cookie, node_debug_id,
                        BR_RELEASE, "BR_RELEASE");
            if (!ret && !weak && has_weak_ref)
                ret = binder_put_node_cmd(
                        proc, thread, &ptr, node_ptr,
                        node_cookie, node_debug_id,
                        BR_DECREFS, "BR_DECREFS");
            if (ret)
                return ret;
         break;
        case BINDER_WORK_DEAD_BINDER:
        case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
        case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: 
            struct binder_ref_death *death;
            uint32_t cmd;
            death = container_of(w, struct binder_ref_death, work);
            if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION)
                cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;
            else
                cmd = BR_DEAD_BINDER;
            binder_stat_br(proc, thread, cmd);
         break;
        
        ...
    return 0;

当Binder驱动和进程或者线程通信时,会把一个task(BINDER_WORK_NODE类型,是一个语Binder实体对象相关的,代表要修改与Binder实体对象所对应的Binder本地对象的引用计数)添加到todo队列中。同时目标进程或者线程就会不断地调用Binder驱动程序的binder_thread_read函数轮询todo队列,有task则去除并返回到用户空间去处理。然后Binder驱动接着就会去根据规则来请求增加或者减少对应Binder本地对象的引用计数值。需要注意的是,对于BR_INCREFS和BR_ACQUIRE协议,Server进程会马上增加Binder本地对象的强引用或弱引用计数,并使用BC_INCREFS_DONE和BC_ACQUIRE_DONE协议通知Binder驱动;而对于BR_RELEASE和BR_DECREFS,Server进程是先将它们缓存在IPCThreadState类的成员变量mPendingStrongDerefs 和mPendingWeakDerefs ,等到Server 进程下次使用IO控制指令BINDER_WRITE_READ进入Binder驱动之前,再来处理,这样处理的原因是想让Server进程优先去处理其他更重要的事。

2、Binder 实体对象(binder_node)生命周期的管理

Binder实体对象类型为binder_node,由内核空间的Binder驱动创建,会**被Binder驱动中的Binder引用对象**所引用。Client进程首次引用一个Binder实体对象时,Binder驱动就会在内部为这个Binder实体对象创建一个对应的Binder引用对象。当Client进程通过Server Manager 来获取一个Server 组件的代理对象时,Binder驱动就会先找到组件对应的Binder实体对象,再创建一个Binder引用对象来引用它,即增加被引用的Binder实体对象的引用计数;而当Client进程不再引用一个Server组件时,就会请求Binder驱动释放之前为所创建的一个Binder引用对象,即减少该Binder引用对象锁引用的Binder实体对象的引用计数。

3、Binder 引用对象(binder_ref)生命周期的管理

Binder引用对象类型为binder_ref,由内核空间的Binder驱动创建,会**被B用户空间的中的Binder代理对象**所引用。Client进程引用了Server进程的一个Binder本地对象时,Binder驱动就会在内部为这个Binder本地对象创建一个对应的Binder引用对象。Binder引用对象运行于内核空间的Binder驱动程序中,而引用了它的Binder代理对象运行在用户空间的Client进程,不能直接管理,也是通过内部的BC_INCREFS、BC_ACQUIRE、BC_DECREFS和BC_RELEASE协议来增加或者减少强引用计数或弱引用计数。

一个Binder实体对象可以有多个Binder(本地对象的)引用对象

4、Binder 代理对象(BpBinder)生命周期的管理

Binder代理对象类型为BpBinder,由用户空间的Client进程创建,与Binder本地对象类似,一方面会被Client进程的其他对象引用(得益于Android 智能指针技术(RefBase),Server进程其他对象可以通过智能指针来引用这些Binder本地对象。),另一方面它会主动去引用Binder驱动的引用对象。因为Binder 驱动的Binder引用对象运行于内核空间,不能直接通过智能指针来引用,也是通过内部的BC_INCREFS、BC_ACQUIRE、BC_DECREFS和BC_RELEASE协议来增加或者减少强引用计数或弱引用计数。

每一个Binder代理对象都是通过一个句柄值与一个Binder引用对象管理的,Client进程就是通过这个句柄值为维护运行在它里面的Binder代理对象的。

Client进程会在内部建立一个handle_entry类型的Binder代理对象列表,以句柄值为关键字来维护其所有的Binder代理对象。其中handle_entry结构体力有一个指向BpBinder的IBinder指针(即Binder代理对象的地址),当Client进程接到驱动传递过来的句柄值,就以这个句柄值在其内部Binder代理列表去检索是否存在对应的handlle_entry结构体,存在则Client进程继续使用该结构体的BpBinder对象与Server进程通信,反之则会先创建一个BpBinder对象并保存至handle_entry结构体中并将该结构体保存到Binder代理对象列表中(这个Binder代理对象类似缓存池,可以避免重复创建Binder代理对象来引用Binder驱动中同一个Binder引用对象)

简而言之,每一个Binder代理对象都对应一个handle_entry结构体实例,结构体的两个成员变量binder和refs,分别指向Binder代理对象以及其内部的一弱引用计数对象。

 struct handle_entry 
                IBinder* binder;
                RefBase::weakref_type* refs;
            ;
BpBinder::BpBinder(int32_t handle): mHandle(handle), mAlive(1), mObitsSent(0), mObituaries(NULL)

    extendObjectLifetime(OBJECT_LIFETIME_WEAK);
    IPCThreadState::self()->incWeakHandle(handle);

当BpBinder 构造函数被调用时,最终还是通过IPCThreadState的incWeakHandle函数增加handle对应的Binder引用对象的弱引用计数,

void IPCThreadState::incWeakHandle(int32_t handle)

    mOut.writeInt32(BC_INCREFS);//内部协议通知给Binder驱动去增加弱引用计数,本质上就是把数据写入到Parcel中
    mOut.writeInt32(handle);

而当BpBinder 析构函数被调用时,也是通过IPCThreadState的decWeakHandle函数去减少的。总之,有一个Binder代理对象可能被用户空间Client进程的其他对象所引用,当其他对象通过强指针来引用时,Client进程需要请求Binder驱动增加引用计数,但为了减少Client进程与驱动的交互开销,只有当Binder代理对象真正第一次被强指针引用时,Client进程才会请求Binder驱动增加对应Binder引用对象的强引用计数

5、Binder本地对象的异常死亡通知机制

理想情况下,通过智能指针结合那八种内部协议就可以正确的维护和管理Binder 中各个对象的生命周期,但是当Client或者Server进程异常死亡时,其依赖关系就会处处,因此需要一种监控Binder本地对象死亡的机制,当监听到Binder本地对象死亡时间后,通知所有引用了这个Binder本地对象的代理对象(也是需要通过IPCThreadState实现)。

5.1、Binder本地对象死亡监听的注册

Binder代理对象在注册它所引用的Binder本地对象的死亡监听之前,首先要定义好死亡通知的接受者,即必须要继承IBinder的内部类DeathRecipient,实现很鉴定单只需要重写其函数binderDied即可。在这个Binder代理对象所引用的Binder本地对象死亡时自动触发binderDied函数。其次就是注册死亡通知接收者,调用Binder代理对象中的linkToDeath函数即可完成注册。

5.2、Binder本地对象死亡的判断及通知

当Server进程启动时会主动函数open_binder来打开binder设备文件/dev/binder/,如果是正常退出会自动去调用close函数来关闭设备文件/dev/binder;而异常退出就没有正常关闭binder文件,那么内核就负责去关闭它,随即触发了函数binder_release,于是Binder驱动在通过在binder_release函数中检查进程退出后是否有Binder本地对象在里面运行,有则表示他们已经是死亡的Binder本地对象了,最后再通过内部协议BR_DEAD_BINDER通知Client进程。

5.3、Binder本地对象死亡的反注册

当Client进程不需要监听一个Binder本地对象的死亡事件时,就可以调用Binder代理对象的unLinkToDeath来反注册前面的一个死亡事件监听。

6、引用计数管理小结

当一个Binder实体对象的引用计数由0变成1或者1变成0时,Binder驱动会请求相应的Service组件增加或者减少其引用计数。

  • 当一个Bidner实体对象请求一个Service组件来执行某一操作时,会增加该Service组件的强或弱引用计数Binder实体对象会将has_strong_ref和has_weak_ref置为1
  • 当一个Service组件完成一个Binder实体对象所请求的操作后,Binder对象会请求减少该Service组件的强或弱引用计数
  • Binder实体对象在请求一个Service组件增加或减少强或弱引用计数的过程中,会将pending_strong_ref或pending_weak_ref置为1,当Service组件完成增加或减少计数时,Binder实体对象会将这两个变量置为0。

以上是关于Android Binder小结的主要内容,如果未能解决你的问题,请参考以下文章

Android 进阶——Framework 核心之Binder 对象及其生命周期小结

细读《深入理解 Android 内核设计思想》Binder 机制 [下]

细读《深入理解 Android 内核设计思想》Binder 机制 [下]

Android AIDL 小结

如何理解 Android Binder 递归

Android Binder 系统学习笔记Binder系统的基本使用方法