摘要:本节主要来讲解Android10.0 Binder的通信原理总结
阅读本文大约需要花费17分钟。
文章首发微信公众号:IngresGe
专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢!
[Android取经之路] 的源码都基于Android-Q(10.0) 进行分析
[Android取经之路] 系列文章:
《系统启动篇》
《日志系统篇》
《Binder通信原理》:
Binder的通信原理基本上都已经说完,这一节我们做一个简单的概要总结。
下图中涉及到Binder模型的4类角色:Binder驱动,ServiceManager,Server和Client。Binder机制的目的是实现IPC(Inter-Process Communication),即Client和Server之间的通信。
其中Server,Client,ServiceManager运行于用户空间,Binder驱动运行于内核空间。这四个角色的关系和互联网类似:Server是服务器,Client是客户终端,ServiceManager是域名服务器(DNS),驱动是路由器。
Binder 通信采用 C/S 架构,从组件视角来说,包含 Client、 Server、 ServiceManager 以及 Binder 驱动,其中 ServiceManager 用于管理系统中的各种服务。
Binder 在 framework 层进行了封装,通过 JNI 技术调用 Native(C/C++)层的 Binder 架构。
Binder 在 Native 层以 ioctl 的方式与 Binder 驱动通讯。
Binder通信流程如下:
在这里其实会存在一个问题,Client和Server之间通信是称为进程间通信,使用了Binder机制,那么Server和ServiceManager之间通信也叫进程间通信,Client和Server之间还会用到ServiceManager,也就是说Binder进程间通信通过Binder进程间通信来完成,这就好比是 孵出鸡前提却是要找只鸡来孵蛋,这是怎么实现的呢?
Binder的实现比较巧妙:预先创造一只鸡来孵蛋:ServiceManager和其它进程同样采用Binder通信,ServiceManager是Server端,有自己的Binder对象(实体),其它进程都是Client,需要通过这个Binder的引用来实现Binder的注册,查询和获取。
ServiceManager提供的Binder比较特殊,它没有名字也不需要注册,当一个进程使用BINDER_SET_CONTEXT_MGR_EXT命令将自己注册成ServiceManager时Binder驱动会自动为它创建Binder实体(这就是那只预先造好的鸡)。
其次这个Binder的引用在所有Client中都固定为0(handle=0)而无须通过其它手段获得。也就是说,一个Server若要向ServiceManager注册自己Binder就必须通过0这个引用号和ServiceManager的Binder通信。
类比网络通信,0号引用就好比域名服务器的地址,你必须预先手工或动态配置好。要注意这里说的Client是相对ServiceManager而言的,一个应用程序可能是个提供服务的Server,但对ServiceManager来说它仍然是个Client。
图片来源csdn-jeanboydev
Binder-IPC机制,就是指在进程间传输数据(binder_transaction_data),一次数据的传输,称为事务(binder_transaction)。
对于多个不同进程向同一个进程发送事务时,这个同一个进程或线程的事务需要串行执行,在Binder驱动中为binder_proc和binder_thread都有todo队列。
也就是说对于进程间的通信,就是发送端把binder_transaction节点,插入到目标进程或其子线程的todo队列中,等目标进程或线程不断循环地从todo队列中取出数据并进行相应的操作。
在Binder驱动层,每个接收端进程都有一个todo队列,用于保存发送端进程发送过来的binder请求,这类请求可以由接收端进程的任意一个空闲的binder线程处理。
接收端进程存在一个或多个binder线程,在每个binder线程里都有一个todo队列,也是用于保存发送端进程发送过来的binder请求,这类请求只能由当前binder线程来处理。
binder线程在空闲时进入可中断的休眠状态,当自己的todo队列或所属进程的todo队列有新的请求到来时便会唤醒,如果是由所需进程唤醒的,那么进程会让其中一个线程处理响应的请求,其他线程再次进入休眠状态。
下面展示了Binder协议码的演变过程,在Android9.0之前,当client向Binder驱动发送BC_TRANSACTION,Binder驱动唤醒Server进程时,会向client进程发送BR_TRANSACTION_COMPLETE,现在这一步被移到了 唤醒Client之后再做,减少了数据延迟。
Android9.0之前的协议码流程:
Android9.0及之后的协议码流程:
上面第5步的 BR_TRANSACTION_COMPLETE 被延迟到 第 10步 ,Android做了deferred_thread_work,延迟 TRANSACTION_COMPLETE,因此不会立即返回到用户空间;这允许目标进程立即开始处理此事务,从而减少延迟。然后,当目标回复(或出现错误)时,我们将返回TRANSACTION_COMPLETE。
AIDL的设计采用了Proxy-Stub(代理-存根) 的设计模式,Client拿到的是Proxy的Binder代理对象,Server拿到的是Stub的Binder服务实体,两者之间的数据传入通过Parcel进行扁平化传输。
Proxy将特殊性接口转换成通用性接口,Stub将通用性接口转换成特殊性接口,二者之间的数据转换通过Parcel(打包)进行的,Proxy常作为数据发送代理,通过Parcel将数据打包发送,Stub常作为数据接收桩,解包并解析Parcel Data package。
Client和Server交互的简单示意流程:
Binder通信的数据流转如下图所示:
AIDL的具体流程如下:
我的微信公众号:IngresGe