前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android Binder 分析系列——原理(上)

Android Binder 分析系列——原理(上)

作者头像
open
发布2020-03-19 17:03:29
1.4K0
发布2020-03-19 17:03:29
举报

作者 介绍

胡明明

专注于AndroidFramework、VR开发。

分析之前说一下原理。为要 android 要搞这么复杂的一个东西。那是因为 android 是个多进程的系统,进程间的数据交换、相互调用(某几个程序配合完成某些业务)就涉及跨进程通信。2个进程不能直接访问数据的原因:

  1. 1、每个进程的地址空间的独立的,所以进程A中某个数据的地址在进程B中不确定是什么东西。
  2. 2、安全性,如果能随便访问其它进程空间的数据,那么是非常危险的事情(想想看你再用支付宝输支付密码的时候,其它随便一个程序就能轻轻松松读取你输入的密码是多么恐怖)。

所以 android 就整一套进程通信框架——binder。

一、原理

首先 binder 在最底层有 kernel 的驱动支持。/dev/binder 是 binder 的设备文件。然后 android 通过这个驱动在 native 层整了一套 C/S 架构的框架出来,最后在 java 对应也封装了一层(可以理解为 native 的马甲)。这些东西后面再慢慢分析。

二、应用

基于 binder android 弄了很多 manager services,不过我觉得倒是因为需要存在这些 maanger services 才需要 binder 进程间通信。这里说说为什么需要这些 manager services(我后面把这些称为:那一票 services)。因为设备的上的有些硬件(例如相机、传感器)一般一次只能一个访问,有些需要把一些数据混合在一起输出(SurfaceFlinger、AudioFlinger),这就需要一些管理,但是应用是不同的程序,它们并不知道其它人的情况,所以就需要一个 manager,而且这个 manager 是要能接受不同进程的。这就引出了 android binder 的最经典的场景—— android 那一票 services。

同时由于有这一票 services 的 存在,那么又要有人来管它们,所以就有一个东西叫: ServiceManager 。这个东西本身也是基于 binder 通信的。

三、框架设计——native

binder 主要的实现在 native 层。先来张图,整体对框架有个概括(图中我省略了 binder 进程 death 之后的通知机制,这个可以后面单独分析,这里只是画出了刚开始比较关键的通信的那部分):

这里先啰嗦下 binder native 层的代码位置(这个是个模块,相关的都里面,没几个文件,自己找吧):

代码语言:javascript
复制

图中分为2个部分:一个是实现部分(BBinder、BpBinder 那部分),一个是接口部分(IInterface 那部分)。先来看下实体部分:

前面说了 binder 是 C/S 架构的,那当然得有 server 和 client。BBinder 代表 server,BpBinder 代表 client,然后在这个基础上抽象出 IBinder 这个基类。IBinder 中比较重要的抽象方法有4个:

代码语言:javascript
复制

然后在基类有默认实现的是3个:

代码语言:javascript
复制

基类的实现还真全是马甲,但是这样也有个好出,就是子类可以只覆盖自己感兴趣的方法就可以了(否则子类就必须全部实现,不然编译会报错的)。

先来看看 localBinder 和 remoteBinder 这2个,这2个看定义就十分明显了,一个是返回 BBinder 的指针(服务器的),一个是返回 BpBinder 的指针(客户端的),而且在 BBinder 和 BpBinder 分别只实现了一个:

代码语言:javascript
复制

然后是 queryLocalInterface 这个,这个返回的是 IInterface 的指针(sp android 搞的啥智能指针,可以参看我前面一篇相关的备忘,挺烦的)。BBinder 和 BpBinder 都没有实现,这个放到后面暴露的接口去实现了(后面再说)。

最后来看下: transact,这个看名字,和参数就知道这个就是通信用的方法。这个在基类中也没实现,但是在 BBinder 和 BpBinder 有实现,并且不一样(当然得不一样,服务器能和客户端一样么)。BBinder 的:

代码语言:javascript
复制

那个 PING_TRANSACTION 估计是测试用的,先不理它,它主要就是调用了 onTransact 这个回调。这个回调是在 BBinder 中定义的,是个虚函数,主要留给它的子类来实现。可以想象得到服务器端在等待客户端的请求,当有请求来的时候,就会出发 onTransact 然后由具体的服务(子类)来实现这个回调,处理不同的逻辑。

而在 BpBinder 中是这样的:

代码语言:javascript
复制

又是马甲,这个IPCThreadState 是线程剧本变量,就是一个线程存一个,不同线程不一样,binder 的 C/S 架构采用了多线程来处理请求,这个也是后面再分析,先来看看实现再说:

代码语言:javascript
复制

这个函数中关键点是 writeTransactionData 和 waitForResponse,这2个函数分别是对 binder 驱动写请求(数据已经通过 Parcel 打包好,这个也是后面再分析),然后等待 binder 驱动的返回的数据结果(服务器那端写的)。驱动相关的也是后面再说,先在继续往下走。这里可以看得出客户端是将请求写入驱动,发送给服务器,然后等待服务器返回的结果。

然后就是接口了,接口基类是 IInterface,这个基类很简单,就定义2个有用的函数:

代码语言:javascript
复制

其实它留给子类的就 onAsBinder 这个回调,用来获取 IBinder 的指针。IInterface 的子类是 BnInterface 和 BpInterface 分别对应 BBinder(服务器) 和 BpBinder(客户端)的接口。在这之前得先看看 INTERFACE 这个东西,android 在这里弄了一个模版类,BnXx 和 BpXx 都是。

在 IInterface.h 中有这2个宏:

代码语言:javascript
复制

如果在 .h 中的 class 定义中调用 DECLARE_META_INTERFACE("Xx") 在 .cpp 中的实现中调用 IMPLEMENT_META_INTERFACE("Xx", "Xx") 那么就相当于声明和实现了:

  1. 1、默认构造函数
  2. 2、析构函数
  3. 3、定义了并以 Xx 初始化 String16 descriptor 这个变量
  4. 4、asInterface: 返回 IXx 的指针
  5. 5、getInterfaceDescriptor: 返回 descriptor 字符串

实际上,后面的具体的 native 的 service 的接口就是这么写的(后面会有具体的实例分析)。

接下来就看真正的接口基类: BnInterface:

代码语言:javascript
复制

哎呦咧,C++ 的多重继承,分别继续了 BBinder(binder 的服务器) 和 INTERFACE 这个其实就是上面的 IInterface,在具体的 services 中会通过 IInterface 中的那2个宏弄一个 IXx 出来,然后 BnInterface 这里的 INTERFACE 就是 IXx(这个后面到了实例分析,就会很清楚了)。这里实现上面 BBinder 那没实现的几个接口:

代码语言:javascript
复制

queryLocalInterface 这个是只有 BBinder 才有的(对比 IBinder 的接口),然后根据在这个函数的实现:对比 descriptor 是不是自己定义的(通过DECLARE_META_INTERFACE 这个宏定义的),然后是否返回自己,可以猜得到:1、这个函数是用来判断请求是不是处于本进程内,如果是的话,应该就不需要跨进程调用,直接可以调用本进程的方法。这样对于上层应用来说,暴露的是 IBinder 接口,上层应用不需要关心调用是本地的还是远程的。2:descriptor 这个是用来区别 binder 的服务器的,binder 通过这个来判断请求是不是发给自己的,如果 descriptor 不匹配,则拒绝处理。(这些猜测后面再慢慢说)

然后是 BpInterface 了:

代码语言:javascript
复制

比 Bn 相比,没了判断是不是本地的接口了,客户端当然不需要有啦。然后应该是继续 BpBinder 的,变成了 BpRefBase ,看到这个名字我又想到了 android 那蛋疼的智能指针和引用计数,我真的很烦这个东西,就不能好好的自己管好内存么??

代码语言:javascript
复制

上面这个东西,其它的不看,就看一个函数 remote() 返回 mRemote 这个 IBinder 指针。然后来看下 BpInterface 的构造函数:

代码语言:javascript
复制

BpInterface 的 sp 参数的构造函数,把 sp 传给 BpRefBase 了,这个 IBinder 其实就是 BpBinder (通过后面的分析可以看得出的)。结合上面 Bn 的 onAsBinder 是返回 this 自己(BBinder),Bp 的是返回 BpBinder 。

再下面,就是具体业务相关的了,IXx 继承自 IInterface,主要是使用 IInterface 提供的那个 DECLARE_META_INTERFACE(Xx) 这个宏声明一些接口(上面有分析的),Xx 就是接口的名字了,例如 ServerManager、SurfaceComposer 之类的(这个后面会有具体的实例分析的)。然后这个 IXx 还得定义这个 service 对外(客户端)提供的接口,例如 CaptureScreen 之类的,这个就和具体的 service 相关了。

BnXx 继承 BnInterface(同时继承 IXx),使用 IInterface 提供的 IMPLEMENT_META_INTERFACE(Xx, Xx) 来实现上面说的那些接口。注意这里的 Xx 要和 DECLARE_META_INTERFACE 那里的一样,例如都叫 SurfaceComposer, 然后后面那个是标示(descriptor),例如 “android.ui.ISurfaceComposer”(用包名来标示一般不会重复)。

然后剩下的主要是实现 onTransact 就是响应客户端的请求。其实通过后面的分析 BnXx 中也并不是真正实现请求的地方,这个只是一个中转站而已,真正的实现在 service 模块里面,一个一个业务函数实现的,这里的 onTransact 只是区分客户端发过来的请求命令,然后去调用 service 里面的函数,实现这些的文件都叫 I Xx.h, IXx.cpp 看上去也不像实现的样子。然后真正的 service 就要继承 BnXx 去实现 onTransact。但是你也发现了,onTransact 已经在 BnXx 里实现了,所以你在 services 看到的 onTransact 都是马甲(有些做了一些拦截,例如权限检测,没权限的请求直接拦截下来),基本上都是调用: super.onTransact 的 -_-|| 。

BpXx 继承 BpInterface。 BpInterface 并没强制要求 BpXx 实现啥东西,但是作用客户端(Java 层那一堆 XxManager 给其它 apk 调用的),暴露给第三方引用使用的,必须要实现服务器提供的方法对应的接口:例如说 service 那边有一个方法是: captureScreen 用来实现截屏用的,那么客户端也必须有响应的方法: captureScreen,然后客户端调用 remote()(IBinder) 的 transact 发送请求到服务器。当然其实函数名字也可以不一一对应,你只要在服务器 onTransact 里调用正确就行了。但是后面你会发现如果这些东西一一对应的话,代码是很机械的,后面 android 就搞了个代码自动生成的工具出来(aidl)。

这里 native 层的框架就说完了。接下来看下 java 层的。见《Android Binder 分析系列——原理(下)》

小贴士

本文由原作者胡明明独家授权Open软件开发小组发布,著作权归原作者所有。如需转载请联系原作者申请授权。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-06-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Open软件开发小组 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、原理
  • 二、应用
  • 三、框架设计——native
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档