图解Binder:事务Binder事务是AndroidIPC(进程间通信)机制的基本单元。它是基于C/S架构

本文基于Androidplatform分支android-13.0.0_r1和内核分支common-android13-5.15解析。

一些关键代码的链接,可能会因为源码的变动,发生位置偏移、丢失等现象。可以搜索函数名,重新进行定位。

Binder事务是AndroidIPC(进程间通信)机制的基本单元。它是基于C/S架构的,主要涉及到两个进程:客户端进程和服务端进程。客户端进程发送一个包含指令和数据的Binder事务请求到服务端进程,服务端进程在收到请求后,进行处理并返回一个Binder事务回复。

你可以认为Binder事务,就是一个进程(客户端)在调用另一个进程(服务端)中的一个函数。这个"函数调用"的过程就是通过发送一个Binder事务来发起,"返回值"就是Binder事务的回复。

Binder驱动在Binder事务中的作用,就像一个中转站,负责转发工作:

本文主要围绕Binder事务,逐个解释它涉及的各种小概念。

Binder消息,即Binder命令,笔者更喜欢称其为消息。二者并无差别。

就像网络请求中的Http报文一样,在进程间传递的Binder消息,也遵循特定的Binder协议。Binder协议是AndroidBinder的底层通信协议,主要在Android用户空间和内核空间之间进行通信。Binder协议定义了一组消息码,每个消息码都对应Binder系统中的一个操作或事件。

Binder的消息通信基于这组消息码。消息的具体格式由Binder协议定义,通常包括消息码和数据两部分:

注意:不是所有的消息码后面,都会有对应的数据,如BR_TRANSACTION_COMPLETE。

binder_write_read的定义如下:

structbinder_write_read{binder_size_twrite_size;binder_size_twrite_consumed;binder_uintptr_twrite_buffer;binder_size_tread_size;binder_size_tread_consumed;binder_uintptr_tread_buffer;};结构体中的字段有以下含义:

在调用BINDER_WRITE_READ这个ioctl命令时,用户空间会设置write_size和write_buffer来指定要写入Binder驱动的数据,设置read_size和read_buffer来指定读取数据的缓冲区。Binder驱动在处理完ioctl命令后,会更新write_consumed和read_consumed字段来反馈实际消耗和返回的字节数。

这个ioctl命令的用途非常广泛,包括但不限于向驱动发送Binder事务请求、接收来自驱动的回复和通知等。

Binder消息都是通过BINDER_WRITE_READ这个ioctl命令发送、读取的。一个BINDER_WRITE_READ里可以发送、读取多个Binder消息。如下:

下面的代码揭示了如何通过一个ioctl()调用,发送BC_TRANSACTION消息。

同步事务是最常见的事务。当一个进程给另一个进程发送同步事务时,它会等待直到事务处理完毕。这意味着在事务处理期间,调用进程会被阻塞。当事务处理完毕后,会有一个回复数据,返回到调用进程,也就是客户端。

大致流程是:

注意:

失败的情况有很多种,比如服务端不存在、客户端已死亡、缓冲区溢出等等。这里举两个可能的例子:

Binder驱动在处理BC_TRANSACTION消息时,发现消息中指向服务端的引用是一个无效引用,就会回复BR_FAILED_REPLY消息给客户端。

Binder驱动在处理服务端的BC_REPLY消息时,发现这时候客户端已经死亡,不需再回复客户端。不过,仍然需要回复BR_TRANSACTION_COMPLETE消息给服务端。

异步事务是一种不需要回复的事务,它会在事务数据的flags中增加一个TF_ONE_WAY标志位。当一个进程发送一个异步事务到另一个进程时,它会立即返回,不需要等待事务的完成。这意味着异步事务是非阻塞的,所以它们对于需要快速响应的场景很有用。然而由于没有回复,客户端无法知道事务何时完成以及是否成功。

事务的数据,通常会包含多种数据类型,主要分为两种:普通的数据类型和对象。

flat_binder_object的定义如下:

structflat_binder_object{structbinder_object_headerhdr;__u32flags;union{binder_uintptr_tbinder;__u32handle;};binder_uintptr_tcookie;};普通的数据类型传递给Binder驱动的时候,会直接拷贝到接收缓冲区。而对象不一样,需要做特殊处理。比如Binder实体传递给Binder驱动的时候,会被逐个处理,转换成Binder引用。

binder_transaction_data的定义如下:

这是一段我们想要发送给其他进程的数据。buffer就是指向这段数据的起始地址。而offsets则是指向数据里offsets数组。offsets[0]、offsets[1]则分别代表数据里两个flat_binder_object的地址相比较于buffer的偏移量,即offset[i]-buffer。

可以发现,int、float、flat_binder_object这些数据并不遵循一定的顺序,写入到缓冲区中。所以,这就要求在服务端里,我们必须按照写入的顺序,读取接收缓冲区里的数据。

Binder实体、Binder代理是Framework层的抽象概念。Binder节点和Binder引用则是内核层的抽象概念。它们的关系如下:

把Binder实体跨进程传递时,其实就是把指向该Binder实体(即BBinder对象)的指针存储在flat_binder_object中,驱动会在为当前进程维护的红黑树里为它创建对应的Binder节点,然后在为目标进程的维护的红黑树里里,创建对应的Binder引用,最后把引用传递给目标进程,并被记录在对应的BpBinder里。

我们想要调用另一个进程的函数,必须先持有另一个进程的能调用这个函数的Binder实体相对应的Binder代理。这就要求另一个进程必须先把该Binder实体传递到我们当前进程。

下图展示了将Binder实体通过一个Binder事务第一次传递给其他进程时所发生的事:

注意,客户端、服务端只是相对而言的。图中是指发起事务的是客户端。当服务端拿到Binder实体的引用时,就可以构建一个对应的Binder代理,即BpBinder,向Binder实体发起事务。这时候,它就是客户端,而Binder实体所在进程就是服务端。

细心的读者,可能会有一个疑问:Binder实体需要由一个事务发送出去。但是事务是要由一个Binder代理发起的,那么最开始的一个Binder代理,它的Binder引用又是从哪里来的呢?

这其实就是个鸡生蛋,蛋生鸡,到底是先有鸡还是先有蛋的问题。ServiceManager就是那只最开始的鸡。后续在介绍ServiceManager时,会解释这个问题。

客户端发起事务:

BpBinder.cpp

└──IPCThreadState.cpp

└───────ioctl.cpp

服务端接收事务:

IPCThreadState.cpp

└────ioctl.cpp

└───IPCThreadState.cpp(这里重新回到用户空间,并完成talkWithdDriver()调用)

└─────Binder.cpp

└────IPCThreadState.cpp(这里完成了BBinder::transact()调用,回到了executeCommand())

└────────后续调用栈与客户端发送BC_TRANSACTION消息类似...

THE END
1.Java03(面向对象消息和方法参数的传递构造函数this关键字)对象是类的实例 属性 对象所共有的特征 eg.王小红是个对象 王小红有性别、年龄、姓名 这些东西同时也是其他人有的 方法 对象共有的行为 eg. 王小红是一个对象 王小红需要吃饭、睡觉、喝水 吃饭、睡觉、喝水也是其他人拥有的行为 消息和方法 消息 也就是参数 数据是通过方法进行传递的 https://blog.csdn.net/Dean_xiu/article/details/107170485
2.穿越消息之路:深入探讨SpringIntegration的魅力消息转换是Spring Integration中常见的操作,用于将消息从一种格式或结构转换为另一种格式或结构,以满足系统的需求。以下是消息转换的实际应用场景和示例: JSON到对象的转换: @Transformer(inputChannel = "jsonInputChannel", outputChannel = "objectOutputChannel")public MyObject convertJsonToObject(String jsonStringhttps://developer.aliyun.com/article/1436168
3.对象存储POST上传对象存储API文档用户除了可以用PUT直接上传对象外,还可以使用POST上传对象。 单次上传对象大小范围是[0, 5GB],如果需要上传超过5GB的大文件,需要通过多段操作来分段上传。 与PUT上传的区别 PUT上传中参数通过请求头域传递;POST上传则作为消息体中的表单域传递。 PUT上传需在URL中指定对象名;POST上传提交的URL为桶域名,无需指定对象https://ecloud.10086.cn/op-help-center/doc/article/64198
4.解析UML动态建模中消息状态图和顺序图顺序图(SequenceDiagram)用来描述对象之间动态的交互关系,着重体现对象间消息传递的时间顺序。UML动态建模中顺序图存在两个轴:水平轴表示不同的对象,垂直轴表示时间。顺序图中的对象用一个带有垂直虚线的矩形框表示,并标有对象名和类名。垂直虚线是对象的生命线,用于表示在某段时间内对象是存在的。对象间的通信通过在https://www.51cto.com/article/210609.html
5.SAPR/3开发类及表/结构资料SWM 工作流:错误消息 SWO 工作流对象类型 SWP 工作流;处理器 SWR 业务工作流:WAPI 接口 SWT 工作流:工作流跟踪功能 SWU 业务工作流:实用程序功能(无关的设备类型) SWUV 桌面对象 SWW 工作流:工作项 SWWW 基础 WWW 项目的开发类 SWX 商业工作流:实用程序 http://www.360doc.com/content/12/1122/08/132971_249458029.shtml
6.面向对象技术中,对象是类的实例。对象有三种成份:()属性和方法面向对象技术中,对象是类的实例。对象有三种成份:()、属性和方法(或操作)。 A. 标识 B. 规则 C. 封装 D. 消息https://tiku.baidu.com/singledetail/5e87adf9aef8941ea76e0584
7.成都市退役军人及其他优抚对象优待证申领工作公告一方面是符合申领条件的对象很多,基层接受申请需要一定的时间,请多关注各级退役军人事务部门发布的消息,以便安排好申请时间。另一方面,优待证从申请到拿到手,有很多环节,包括建档立卡、申请信息材料核实、审核、制作,以及分送运输到各退役军人服务站等,需要一定的时间。 https://tyjrswj.chengdu.gov.cn/cdstyjrj/c0304/2022-04/13/content_af2618ef18424f6aaf27d1329d569674.shtml
8.这9类优抚对象的抚恤金和生活补助标准又提高啦!赶紧把好消息转给TA赶紧把好消息转给TA 日前,省民政厅发布《关于调整部分优抚对象等人员抚恤和生活补助标准的通知》,从2017年10月1日起调整部分优抚对象等人员抚恤和生活补助标准。 详情如下↓↓ 各类优抚对象补助标准调整情况 1、各等级残疾军人(含伤残人民警察、伤残国家机关工作人员、伤残民兵民工)的残疾抚恤金在原标准的基础上增加10http://app.chuzhou.cn/?action=show&app=article&contentid=336487&controller=article
9.消息分类标准通知消息管理规则推送服务便捷生活类App 运动健康类App 音乐类App 游戏类App 摄影类App 版本更新说明 使用入门 开发服务号消息接口 服务号消息回调通知接口 订单回传接口 批量查询关注者列表接口 查询消息回执接口 实时运动Bundle对象键值 基础能力支持的国家/地区 扩展能力支持的国家/地区 设备接入 说明 运动设备https://developer.huawei.com/consumer/cn/doc/HMSCore-Guides/message-classification-0000001149358835
10.OracleEBSForm个性化开发配置文件:${ps.}消息:${ms.} 插入项目值按钮只获取item对象的值,得到的表达式是常见的 :数据库.数据项形式。例如:OPERATING_UNITS.OPERATING_UNIT 1.验证按钮:判断条件框中的条件是否成立。 2.现在申请按钮:使设置的个性化规则立即生效,不用在重新打开和关闭Form界面,查看个性化规则是否起作用。 https://blog.itpub.net/7580948/viewspace-2214988/
11.Android的消息机制——Handler★第二种情况:消息队列按照消息处理等待时间顺序(从小到大)进行排列。因此插入新消息需要遍历消息队列,找到新消息插入的位置。 2)读取消息(删除消息):next。 4、Handler类:主要负责处理和发送消息。 1)构造方法 构造方法总结:拿到该线程的looper对象、以及和looper对象绑定MQ。 https://www.jianshu.com/p/98e7bb6024fb