Binder初探

提到Binder不得不说进程间通信(IPC,Interprocess communication)了,Linux现有管道、消息队列、共享内存、套接字、信号量、信号这些IPC机制,Android额外还有Binder IPC机制,Android OS中的Zygote进程的IPC采用的是Socket机制,在上层system server、media server以及上层App之间更多的是采用Binder IPC方式来完成跨进程间的通信。

问题1:安卓的出现时晚于linux和windows的,既然有这么多成熟和现成的ipc,为什么安卓要用Binder呢?

参考自为什么 Android 要采用 Binder 作为 IPC 机制?

先简单概括性地说说Linux现有的所有进程间IPC方式:

1.管道:在创建时分配一个page大小的内存,缓存区大小比较有限;

2.消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;

3.共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;

4.套接字:作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;

5.信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

6.信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;

(1)从性能的角度 数据拷贝次数:Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,但共享内存方式一次内存拷贝都不需要;从性能角度看,Binder性能仅次于共享内存。

(2)从稳定性的角度 Binder是基于C/S架构的,简单解释下C/S架构,是指客户端(Client)和服务端(Server)组成的架构,Client端有什么需求,直接发送给Server端去完成,架构清晰明朗,Server端与Client端相对独立,稳定性较好;而共享内存实现方式复杂,没有客户与服务端之别, 需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题;从这稳定性角度看,Binder架构优越于共享内存。

(3)从安全的角度 传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;而Android作为一个开放的开源体系,拥有非常多的开发平台,App来源甚广,因此手机的安全显得额外重要;对于普通用户,绝不希望从App商店下载偷窥隐射数据、后台造成手机耗电等等问题,传统Linux IPC无任何保护措施,完全由上层协议来确保。 Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志,前面提到C/S架构,Android系统中对外只暴露Client端,Client端将任务发送给Server端,Server端会根据权限控制策略,判断UID/PID是否满足访问权限,目前权限控制很多时候是通过弹出权限询问对话框,让用户选择是否运行。

(4)从语言层面的角度 大家多知道Linux是基于C语言(面向过程的语言),而Android是基于Java语言(面向对象的语句),而对于Binder恰恰也符合面向对象的思想,将进程间通信转化为通过对某个Binder对象的引用调用该对象的方法,而其独特之处在于Binder对象是一个可以跨进程引用的对象,它的实体位于一个进程中,而它的引用却遍布于系统的各个进程之中。可以从一个进程传给其它进程,让大家都能访问同一Server,就像将一个对象或引用赋值给另一个引用一样。Binder模糊了进程边界,淡化了进程间通信过程,整个系统仿佛运行于同一个面向对象的程序之中。从语言层面,Binder更适合基于面向对象语言的Android系统,对于Linux系统可能会有点“水土不服”。

问题2:Binder的原理是什么?

关于这个问题首先来看看ip/tcp的服务连接过程,一图胜千言。

ip/tcp的服务连接过程

为什么我要画这张图,而且还要请包涵我画的这么丑。因为Binder的通信机制和它实在是很像,首先你客户端通过路由去DNS去查询域名相对应的ip地址,最起码你要把你的域名和对应的ip注册给DNS吧,不然人家怎么知道你的ip是多少。

至于client是否会发起查询要看client知不知道ip了,比如windows中的C:\Windows\System32\drivers\etc\hosts文件如果保存了:https://www.baidu.com对应的ip是127.0.0.1那么你就访问自己的主机吧。

这个原理和asInterface很像:

用于将服务端的Binder对象装换成客户端所需的aidl借口类型的对象,这种转换过程是区分进程的,如果客户端和服务端位于同一个进程,也就是obj.queryLocalInterface()返回的不为空,那么此方法返回的就是服务端的Stub对象本身,否则返回的就是系统封装的Stub.Proxy对象。-------《安卓开发艺术探索》

现在再来和Binder机制做一个类比:

Binder驱动--------------路由器

Server Manager---------DNS

Binder Client-----------客户端

Binder Server---------服务端

3、Binder使用场景(举一个Activity中startService的栗子)

Activity:startService()--->ContextWrapper:startService(Intent service)------->ContextImpl:startService(Intent service)------>ContextImpl:startServiceCommon(Intent service, UserHandle user)------>ActivityManagerNative.getDefault().startService()--->ActivityManagerProxy.startService()----mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);-------Binder ipc调用------>ActivityManagerNative:onTransact()------->AMS:startService().

1、注册服务:首先AMS注册到ServiceManager。该过程:AMS所在进程(system_server)是客户端,ServiceManager是服务端。

2、获取服务:Client进程使用AMS前,须先向ServiceManager中获取AMS的代理类AMP。该过程:AMP所在进程(app process)是客户端,ServiceManager是服务端。

3、使用服务: app进程根据得到的代理类AMP,便可以直接与AMS所在进程交互。该过程:AMP所在进程(app process)是客户端,AMS所在进程(system_server)是服务端。


参考书籍:

《安卓开发艺术探索》、《深入理解Android内核设计思想》

参考链接:袁辉辉的博客

你可能感兴趣的:(Binder初探)