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

Posted CrazyMo_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 进阶——Framework 核心之Binder 对象及其生命周期小结的主要内容,如果未能解决你的问题,请参考以下文章

Android 进阶——系统启动之Framework 核心ActivitityManagerService服务启动

Android 进阶——Framework核心 之Binder Java成员类详解

Android 进阶——Framework核心 之Binder Java成员类详解

Android 进阶——Framework 核心之Touch事件分发机制详细攻略

Android 进阶——Framework 核心之Touch事件分发机制详细攻略

Android 进阶——Framework 核心之Touch事件分发机制详细攻略