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

news2024/9/22 17:38:37

文章大纲

  • 引言
  • 一、Binder概述
  • 二、Binder 对象
  • 三、Binder 对象生命周期的管理
    • 1、Binder本地对象(BBinder)的生命周期管理
    • 2、Binder 实体对象(binder_node)生命周期的管理
    • 3、Binder 引用对象(binder_ref)生命周期的管理
    • 4、Binder 代理对象(BpBinder)生命周期的管理
    • 5、Binder本地对象的异常死亡通知机制
      • 5.1、Binder本地对象死亡监听的注册
      • 5.2、Binder本地对象死亡的判断及通知
      • 5.3、Binder本地对象死亡的反注册
    • 6、引用计数管理小结

引言

前面几篇文章都数次提到了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。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/341581.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ChatGPT入门案例|商务智能对话客服(一)

ChatGPT是人工智能研究实验室OpenAI新推出的一种人工智能技术驱动的自然语言处理工具,使用了Transformer神经网络架构,也是GPT-3.5架构,这是一种用于处理序列数据的模型,拥有语言理解和文本生成能力,尤其是它会通过连接…

32个关于FPGA的学习网站

语言类学习网站 1、HDLbits 网站地址:https://hdlbits.01xz.net/wiki/Main_Page 在线作答、编译的学习Verilog的网站,题目很多,内容丰富。非常适合初学Verilog的人!!! 2、牛客网 网站地址:http…

2.12、进程互斥的软件实现方法

学习提示: 理解各个算法的思想、原理结合上小节学习的 “实现互斥的四个逻辑部分”,重点理解各算法在进入区、退出区都做了什么分析各算法存在的缺陷(结合 “实现互斥要遵循的四个原则” 进行分析) 1、单标志法 算法思想:两个进…

SonicWall:请立即修复SMA 1000 漏洞

近日,网络安全供应商SonicWall发布了关于安全移动访问 (SMA) 1000设备的三个安全漏洞的紧急报告,其中包括一个高威胁性的身份验证绕过漏洞。SonicWall指出,攻击者可以利用这些漏洞绕过授权,并可能破坏易受攻击的设备。 从报告中可…

Cow Acrobats ( 临项交换贪心 )

题目大意: N 头牛 , 每头牛有一个重量(Weight)和一个力量(Strenth) , N头牛进行排列 , 第 i 头牛的风险值为其上所有牛总重减去自身力量 , 问如何排列可以使最大风险值最小 , 求出这个最小的最大风险值&am…

Java JCP

Java JCP目录概述需求:设计思路参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy,skip hardness,make a better result,wait for change,challenge Survive. happy for hardess to solv…

GIS在地质灾害危险性评估与灾后重建中的实践技术应用及python机器学习灾害易发性评价模型建立与优化

地质灾害是指全球地壳自然地质演化过程中,由于地球内动力、外动力或者人为地质动力作用下导致的自然地质和人类的自然灾害突发事件。由于降水、地震等自然作用下,地质灾害在世界范围内频繁发生。我国除滑坡灾害外,还包括崩塌、泥石流、地面沉…

手动部署ECS线上数据库及使用

文章参考自手动部署MySQL数据库(CentOS 7) (aliyun.com)MySQL是一个关系型数据库管理系统,常用于LAMP和LNMP等网站场景中。本教程介绍如何在Linux系统ECS实例上安装、配置以及远程访问MySQL数据库。前提条件已创建一台ECS实例。具体操作&…

[ 常用工具篇 ] CobaltStrike(CS神器)基础(一) -- 安装及设置监听器详解

🍬 博主介绍 👨‍🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~ ✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 🎉点赞➕评论➕收藏 养成习…

YOLO-V5轻松上手

之前介绍了YOLO-V1~V4版本各做了哪些事以及相较于之前版本的改进。有的人或许会想“直接学习最近版本的算法不好吗”,在我看来,每一个年代的版本/算法都凝聚着当年学术界的智慧,即便是它被淘汰了也依旧有值得思考的地方,或是可以使…

工业无线物联网解决方案中的缺陷可让攻击者深入访问OT网络

运营技术 (OT) 团队通常通过无线和蜂窝解决方案将工业控制系统 (ICS) 连接到远程控制和监控中心,这些解决方案有时带有供应商运行的基于云的管理界面。 这些连接解决方案,也称为工业无线物联网设备,增加了 OT 网络的攻击面,并且可…

CentOS8基础篇6:文件属性的读取和修改

一、文件属性 每一行代表对应文件或目录的详细信息。从左到右具体的含义是文件属性、文件数、拥有者、所属的组、文件大小、建立月份、建立日期、建立年份或时间、以及文件名。其中“建立年份或时间”字段,如果文件是今年建立的,则显示具体时间&#xf…

文献阅读笔记 # GraphCodeBERT: Pre-training Code Representations with Data Flow

《GraphCodeBERT: Pre-training Code Representations with Data Flow》ICLR 2021 (TH-CPL A会,公认顶会,但未上 CCF 榜)作者部分与 CodeBERT 重叠,主要来自 MSRA Intern 和 MSRA 研究员,学生来自国内多个顶尖高校。资源&#xff…

java 黑马头条 day3 实名认证分布式事务问题 seata

1 完善实名认证功能 1.1 实名认证分布式事务问题 1.1.1 问题分析 在昨天的实名认证代码中,审核完毕后添加 id5的演示异常,重新使用postman进行测试, 会发现 出现异常后 本地方法因为有 Transactional注解 对ap_user ap_user_realname的操作会回滚 而…

【Docker】配置lsky pro兰空图床

Docker 部署 lsky pro 兰空图床的教程 本文首发于 慕雪的寒舍 1.使用Docker部署 命令比较简单,用下面的即可 docker run -d \ --name lsky \ -p 14728:80 \ -v /root/docker/lsky:/var/www/html \ halcyonazure/lsky-pro-docker:latest--name 设置容器名字为 lsky…

基于VS调试分析 + 堆栈观察问题代码段

文章目录问题代码段1 —— 阶乘之和问题代码段2 —— 越界的危害① 发现问题② 分析问题③ 思考问题【⭐堆栈原理⭐】④ 解决问题【DeBug与Release】👨程序员与测试人员👩✒总结与提炼问题代码段1 —— 阶乘之和 先来看一道C语言中比较基础的题目&#x…

新能源汽车PK燃油汽车,首次胜出,输赢真的那么重要?

新能源汽车PK燃油汽车,输赢真的那么重要?是的。【科技明说 | 每日看点】今天看到一个新能源汽车领域的消息,我觉得很有意思,是说中国新能源汽车满意度水平首次超过了燃油汽车,你们觉得是这样么?…

语义通信:DeepSC用于文本传输也太香了吧

论文标题:Deep Learning Enabled Semantic Communication Systems 论文链接:https://arxiv.org/abs/2006.10685v1 摘要 最近,人们开发了支持深度学习的端到端(E2E)通信系统,以合并传统通信系统中的所有物…

React中JSX的用法和理解

React的特点 React是用于构建用户界面的高效且灵活的 JavaScript 库,采用组件化模式和声明式编码;使用DOMdiff算法,最大限度地减少与DOM的交互。 相关js库 react.js:React核心库。react-dom.js:提供操作DOM的react扩…

Eclipse导出jar时的错误

文章目录一、发现问题二、解决问题三、新的问题今天,本来是风和日丽,轻风和畅的,复习的一天,直到我开始尝试导出 jar 可执行文件,兄弟们请记住这个词,我将被他折磨很久。一、发现问题 首先,我美…