Android Binder之应用层精彩解析

来源:互联网 发布:北京java开发工程师 编辑:程序博客网 时间:2024/05/22 01:32


今日科技快讯


中国前四大手机厂商华为公司、小米公司、OPPO以及VIVO今年的全球手机总出货量将突破4亿部,预计占据中国手机总出货量的60%以上其中,华为明年将在全球手机市场排名第三。OPPO和VIVO将延续出色表现,明年保持在前五行列。


作者简介


明天就是周末啦,提前祝大家周末愉快!

本篇依旧来自 疯狂的程序员图文结合分析了Binder机制,并引用一些精彩博客,希望对大家有所帮助

疯狂的程序员 的博客地址:

http://blog.csdn.net/qian520ao


概述


文章对AIDL进行运用与分析,对于Android跨进程通讯有了不少的了解,深入分析 AIDL 通讯过程,我们发现跨进程的主导元素是 Binder,但是这个 Binder 是 Android 中很重要又很复杂的概念,本文就不深入到底层和驱动层去研究,有兴趣的童鞋可以滑动到文章底部,有详细的 Binder 深入探究的文章推荐哦。


为何选择Binder


Linux已经拥有管道,system V IPC,socket等IPC手段,却还要倚赖Binder来实现进程间通信,说明Binder具有无可比拟的优势。

传输性能好

Binder 很重要的的优点之一就是,复杂数据类型传递可以复用内存。

  • socket:是一个通用接口,导致其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信

  • 管道和消息队列:因为采用存储转发方式,所以至少需要拷贝2次数据,效率低;

  • 共享内存:虽然在传输时没有拷贝数据,但其控制机制复杂。

安全性高

传统IPC没有任何安全措施,完全依赖上层协议来确保。首先传统IPC的接收方无法获得对方进程可靠的UID/PID(用户ID/进程ID),从而无法鉴别对方身份。

Android为每个安装好的应用程序分配了自己的 UID,故进程的 UID 是鉴别进程身份的重要标志。可靠的身份标记只有由 IPC 机制本身在内核中添加。

传统 IPC 访问接入点是开放的,无法建立私有通道。Binder 可以使用匿名 Binder 建立私密通道,别的进程就无法通过穷举或猜测等任何方式获得该 Binder 的引用,向该 Binder 发送请求。


Binder总体架构


在Android系统中,这个运行在内核空间的,负责各个用户进程通过Binder通信的内核模块叫做Binder驱动,Binder驱动虽然默默无闻,却是通信的核心。尽管名叫‘驱动’,实际上和硬件设备没有任何关系,只是实现方式和设备驱动程序是一样的。

面向对象思想的引入将进程间通信转化为通过对某个Binder对象的引用调用该对象的方法,而其独特之处在于Binder对象是一个可以跨进程引用的对象,它的实体位于一个进程中,而它的引用却遍布于系统的各个进程之中。最诱人的是,这个引用和java里引用一样既可以是强类型,也可以是弱类型,而且可以从一个进程传给其它进程,让大家都能访问同一Server,就象将一个对象或引用赋值给另一个引用一样。Binder模糊了进程边界,淡化了进程间通信过程,整个系统仿佛运行于同一个面向对象的程序之中。形形色色的Binder对象以及星罗棋布的引用仿佛粘接各个应用程序的胶水,这也是Binder在英文里的原意。

(敲黑板)首先我们要理解我们说的 Binder 分为 Binder对象 和 Binder驱动,即 Binder驱动 就是主要的内核模块,而这个 Binder对象 是通讯的载体,可以自由的通过 Binder驱动 自由穿梭任意进程。所以客户端或者服务器就可以把数据放入Binder对象里,然后进行调用和通讯。类似于胞吞胞吐吧。

Binder框架定义了四个角色:Server,Client,ServiceManager(以后简称SMgr)以及Binder驱动。其中 Server,Client,SMgr 运行于用户空间,驱动运行于内核空间。这四个角色的关系和互联网类似:Server是服务器,Client是客户终端,SMgr是域名服务器(DNS),驱动是路由器。

图片摘自

Binder 总体架构及相关代码浅析

https://www.qcloud.com/community/article/546217

和 DNS 类似,SMgr 的作用是将字符形式的 Binder 名字转化成 Client 中对 该Binder 的引用,使得 Client 能够通过 Binder名字 获得对 Server 中 Binder实体的引用。注册了名字的Binder 叫 实名Binder,就象每个网站除了有IP地址外还有自己的网址。Server创建了Binder实体,为其取一个字符形式,可读易记的名字,将这个Binder连同名字以数据包的形式通过 Binder驱动 发送给 SMgr,通知 SMgr 注册一个名叫张三的 Binder,它位于某个 Server 中。驱动为这个穿过进程边界的 Binder 创建位于内核中的实体节点以及 SMgr 对实体的引用,将名字及新建的引用打包传递给 SMgr。SMgr 收数据包后,从中取出名字和引用填入一张查找表中。Server 向 SMgr 注册了 Binder 引用及其名字后,Client 就可以通过名字获得该 Binder 的引用了。


Binder原理


Binder 通信采用 C/S架构,从组件视角来说,包含 Client、Server、ServiceManager 以及 binder驱动,其中 ServiceManager 用于管理系统中的各种服务。架构图如下所示:

可以看出无论是注册服务和获取服务的过程都需要 ServiceManager,需要注意的是此处的Service Manager 是指 Native层 的 ServiceManager(C++),并非指 framework层 的 ServiceManager(Java)。ServiceManager 是整个 Binder 通信机制的大管家,是 Android 进程间通信机制 Binder 的守护进程,要掌握 Binder机制,首先需要了解系统是如何首次启动 Service Manager。当 Service Manager 启动之后,Client端 和 Server端 通信时都需要先获取 Service Manager 接口,才能开始通信服务。

图中 Client/Server/ServiceManage 之间的相互通信都是基于 Binder 机制。既然基于 Binder 机制通信,那么同样也是C/S架构,则图中的3大步骤都有相应的 Client端 与 Server端。

注册服务(addService):Server进程要先注册Service到ServiceManager。该过程:Server是客户端,ServiceManager是服务端。

获取服务(getService):Client 进程使用某个 Service 前,须先向 ServiceManager 中获取相应的 Service。该过程:Client 是客户端,ServiceManager 是服务端。

使用服务:Client 根据得到的 Service 信息建立与 Service 所在的 Server 进程通信的通路,然后就可以直接与 Service 交互。该过程:client 是客户端,server 是服务端。

图中的 Client,Server,Service Manager 之间交互都是虚线表示,是由于它们彼此之间不是直接交互的,而是都通过与 Binder驱动 进行交互的,从而实现IPC通信方式。其中 Binder驱动 位于内核空间,Client,Server,Service Manager 位于用户空间。Binder驱动 和 Service Manager 可以看做是 Android 平台的基础架构,而 Client 和 Server 是 Android 的应用层,开发人员只需自定义实现 client、Server端,借助 Android 的基本平台架构便可以直接进行IPC通信。

引自

小米系统工程师–Gityuan

http://gityuan.com/2015/10/31/binder-prepare


Binder通信模型


看到这里想必对Binder的一个整体构架有了大致的了解,另外通过

田维术博客

http://weishu.me/2016/01/12/binder-index-for-newer

的一个栗子来描述一下整体过程。

回想一下日常生活中我们通信的过程:假设 A 和 B 要进行通信,通信的媒介是打电话(A是Client,B是Server);A 要给 B 打电话,必须知道 B 的号码,这个号码怎么获取呢?通信录.

先查阅通信录,拿到 B 的号码;才能进行通信;否则,怎么知道应该拨什么号码?回想一下古老的电话机,如果 A 要给 B 打电话,必须先连接通话中心,说明给我接通 B 的电话;这时候通话中心帮他呼叫 B;连接建立,就完成了通信。

另外,光有电话和通信录是不可能完成通信的,没有基站支持;信息根本无法传达。

我们看到,一次电话通信的过程除了通信的双方还有两个隐藏角色:通信录和基站。Binder 通信机制也是一样:两个运行在用户空间的进程要完成通信,必须借助内核的帮助,这个运行在内核里面的程序叫做 Binder驱动,它的功能类似于基站;通信录呢,就是一个叫做 ServiceManager 的东西(简称SMgr)

整个通信步骤如下:

  1. SM建立(建立通信录);首先有一个进程向驱动提出申请为SM;驱动同意之后,SM进程负责管理Service(注意这里是Service而不是Server,因为如果通信过程反过来的话,那么原来的客户端Client也会成为服务端Server)不过这时候通信录还是空的,一个号码都没有。

  2. 各个 Server 向SM注册(完善通信录);每个Server端进程启动之后,向SM报告,我是zhangsan, 要找我请返回0x1234(这个地址没有实际意义,类比);其他Server进程依次如此;这样SM就建立了一张表,对应着各个Server的名字和地址;就好比B与A见面了,说存个我的号码吧,以后找我拨打10086;

  3. Client想要与Server通信,首先询问SM;请告诉我如何联系zhangsan,SM收到后给他一个号码0x1234;Client收到之后,开心滴用这个号码拨通了Server的电话,于是就开始通信了。

Server 进程里面的 Binder 对象指的是 Binder 本地对象,Client 里面的对象指的是 Binder代理对象;在 Binder对象 进行跨进程传递的时候,Binder驱动 会自动完成这两种类型的转换;因此 Binder驱动 必然保存了每一个跨越进程的 Binder对象 的相关信息;在驱动中,Binder本地对象 的代表是一个叫做 binder_node的数据结构,Binder代理对象 是用 binder_ref 代表的;有的地方把 Binder本地对象 直接称作 Binder实体,把 Binder代理对象直接称作 Binder引用(句柄)

ServiceManager与实名Binder

细心的读者可能会发现其中的蹊跷:SMgr 是一个进程,Server 是另一个进程,Server 向SMgr 注册 Binder 必然会涉及进程间通信。当前实现的是进程间通信却又要用到进程间通信,这就好象蛋可以孵出鸡前提却是要找只鸡来孵蛋。Binder 的实现比较巧妙:预先创造一只鸡来孵蛋:SMgr 和其它进程同样采用Binder通信,SMgr 是 Server端,有自己的 Binder对象(实体),其它进程都是 Client,需要通过这个 Binder 的引用来实现 Binder 的注册,查询和获取。SMgr 提供的 Binder 比较特殊,它没有名字也不需要注册,当一个进程使用 BINDER_SET_CONTEXT_MGR命令 将自己注册成 SMgr 时 Binder驱动 会自动为它创建 Binder实体(这就是那只预先造好的鸡)。其次这个 Binder 的引用在所有 Client 中都固定为0而无须通过其它手段获得。也就是说,一个 Server 若要向 SMgr 注册自己 Binder 就必需通过0这个引用号和 SMgr 的 Binder通信。类比网络通信,0号引用就好比域名服务器的地址,你必须预先手工或动态配置好。要注意这里说的Client是相对SMgr而言的,一个应用程序可能是个提供服务的 Server,但对 SMgr 来说它仍然是个 Client。

  • 首先,Server 在自己的进程中向 Binder驱动 申请创建一个 Server 的 Binder的实体。

  • Binder驱动 为这个 Server 创建位于内核中的 Binder实体节点以及 Binder的引用。(在Binder驱动 中创建一块内存)

  • 然后 Server 通过 0 这个引用号和 SMgr的Binder通信 将名字和新建的引用打包传递给SM(实体没有传给SM),通知SM注册一个名叫XXX的 Server。

  • SM 收到数据包后,从中取出 Server名字和引用,填入一张查找表中。

Server 初始化的时候,SMgr 做了一下操作:

  1. 为 binder 分配128k的内存

  2. 通知 binder驱动,使自身成为 binder驱动的“DNS”

  3. 维护一个监听 Server 的死循环,并且维护持有所有 Server 句柄的 svclist

  4. 添加 Server 的时候,进行权限,内存(充足)进行判断,如果没有添加过则将 Server 添加至 svclist。

Client获得实名Binder的引用

Server 向 SMgr 注册了 Binder 引用及其名字后,Client 就可以通过名字获得该 Binder 的引用了。Client 也利用保留的 0号引用 向 SMgr 请求访问某个 Binder:我申请获得名字叫张三的 Binder 的引用。SMgr 收到这个连接请求,从请求数据包里获得 Binder 的名字,在查找表里找到该名字对应的条目,从条目中取出 Binder 的引用,将该引用作为回复发送给发起请求的 Client。从面向对象的角度,这个 Binder对象 现在有了两个引用:一个位于 SMgr 中,一个位于发起请求的 Client中。如果接下来有更多的 Client 请求该 Binder,系统中就会有更多的引用指向 该Binder,就象 java 里一个对象存在多个引用一样。而且类似的这些指向 Binder 的引用是强类型,从而确保只要有引用 Binder实体 就不会被释放掉。通过以上过程可以看出,SMgr 象个火车票代售点,收集了所有火车的车票,可以通过它购买到乘坐各趟火车的票-得到某个 Binder 的引用。

Client 与 Server通讯

Client 向 SM 发送申请服务 Server 的请求,那么 SM 就可以在查找表中找到 该Service 的 Binder引用,并把 Binder引用 (BpBinder)返回给 Client,此时 Client 便可以通过这个引用向 Server(间接)发起调用,Binder引用 将参数包装然后交给驱动并获取 Server 的调用结果。

Binder的线程管理

每个 Binder 的 Server进程 会创建很多线程来处理 Binder请求,可以简单的理解为创建了一个 Binder 的线程池吧(虽然实际上并不完全是这样简单的线程管理方式),而真正管理这些线程并不是由这个 Server端 来管理的,而是由 Binder驱动 进行管理的。

一个进程的 Binder 线程数默认最大是16,超过的请求会被阻塞等待空闲的 Binder线程。理解这一点的话,你做进程间通信时处理并发问题就会有一个底,比如使用ContentProvider 时(又一个使用 Binder机制 的组件),你就很清楚它的 CRUD(创建、检索、更新和删除)方法只能同时有16个线程在跑。(应用与 ContentProvider 为不同进程时)

摘自goeasyway

Android面试一天一题–Binder

http://www.jianshu.com/p/c7bcb4c96b38

整体通讯流程图

参考超强的女博主[Android Bander设计与实现]

http://blog.csdn.net/universus/article/details/6211589

最后可以通过该博主写的博文进一步对Binder进行探索和了解,比如Binder 内存映射和接收缓存区管理,Binder在驱动/传输中的表述等,因为该篇文章主要是在应用层做的一个分析和总结。下面来看一下原图(上面3张图借鉴此图)。


总结


本篇博客图文并茂的总结了Binder应用层的总体流程,总的来说算是对大佬们的文章做的一个总结笔记,Binder非三日之功可破冰,个人觉得在it上的学习也是一样,可能短期的学习并不能展现出有多大改变,但是循序渐进,量变引起质变。骐骥一跃,不能十步;驽马十驾,功在不舍。


欢迎长按下图 -> 识别图中二维码

或者 扫一扫 关注我的公众号

原创粉丝点击