社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
目录
Binder学习指南(文章8:讲得清晰明白,就是根据这篇文章终于了解了binder机制)
Android Bander设计与实现 - 设计篇(文章1:阅读量12w,211赞,全面)
一篇文章了解相见恨晚的 Android Binder 进程间通讯机制(文章2:阅读量5w,76赞,全面)
写给 Android 应用工程师的 Binder 原理剖析(文章7:由浅入深很详细,有代码实例)
BAT大咖助力Android面试6-Binder面试详解(文章3:阅读量不多,详细,有代码实例)
Android面试题(二)——IPC机制(文章4:阅读量4568,全面)
android面试(6)-Binder机制(文章5:阅读量2533,)
如何在android面试中说清楚android中binder机制的实现过程(文章6:阅读量1217,很详细)
为了保证 安全性 & 独立性,一个进程不能直接操作或者访问另一个进程,即Android的进程是相互独立、隔离的。
进程内 用户空间 & 内核空间 进行交互 需通过 系统调用,主要通过函数:
copy_from_user():将用户空间的数据拷贝到内核空间
copy_to_user():将内核空间的数据拷贝到用户空间
(1)Android使用的Linux内核拥有着非常多的跨进程通信机制
(2)性能
(3)安全
Android Binder是用来做进程通信的,Android的各个应用以及系统服务都运行在独立的进程中,它们的通信都依赖于Binder。
为什么选用Binder,在讨论这个问题之前,我们知道Android也是基于Linux内核,Linux现有的进程通信手段有以下几种:
既然有现有的IPC方式,为什么重新设计一套Binder机制呢。主要是出于以上三个方面的考量:
Binder学习指南(文章8:讲得清晰明白,就是根据这篇文章终于了解了binder机制)
对于跨进程通信的双方,我们姑且叫做Server进程(简称Server),Client进程(简称Client);由于进程隔离的存在,它们之间没办法通过简单的方式进行通信,那么Binder机制是如何进行的呢?
回想一下日常生活中我们通信的过程:假设A和B要进行通信,通信的媒介是打电话(A是Client,B是Server);A要给B打电话,必须知道B的号码,这个号码怎么获取呢?通信录.
这个通信录就是一张表;内容大致是:
1 B -> 12345676
2 C -> 12334354
先查阅通信录,拿到B的号码;才能进行通信;否则,怎么知道应该拨什么号码?回想一下古老的电话机,如果A要给B打电话,必须先连接通话中心,说明给我接通B的电话;这时候通话中心帮他呼叫B;连接建立,就完成了通信。
另外,光有电话和通信录是不可能完成通信的,没有基站支持;信息根本无法传达。
我们看到,一次电话通信的过程除了通信的双方还有两个隐藏角色:通信录和基站。Binder通信机制也是一样:两个运行在用户空间的进程要完成通信,必须借助内核的帮助,这个运行在内核里面的程序叫做Binder驱动,它的功能类似于基站;通信录呢,就是一个叫做ServiceManager的东西(简称SM)
OK,Binder的通信模型就是这么简单,如下图:
整个通信步骤如下:
那么Binder驱动干什么去了呢?这里Client与SM的通信,以及Client与Server的通信,都会经过驱动,驱动在背后默默无闻,但是做着最重要的工作。驱动是整个通信过程的核心,因此完成跨进程通信的秘密全部隐藏在驱动里面;这个我们稍后讨论。
OK,上面就是整个Binder通信的基本模型;做了一个简单的类比,当然也有一些不恰当的地方,(比如通信录现实中每个人都有一个,但是SM整个系统只有一个;基站也有很多个,但是驱动只有一个);但是整体上就是这样的;我们看到其实整个通信模型非常简单。
上文给出了Binder的通信模型,指出了通信过程的四个角色: Client, Server, SM, driver; 但是我们仍然不清楚Client到底是如何与Server完成通信的。
两个运行在用户空间的进程A和进程B如何完成通信呢?内核可以访问A和B的所有数据;所以,最简单的方式是通过内核做中转;假设进程A要给进程B发送数据,那么就先把A的数据copy到内核空间,然后把内核空间对应的数据copy到B就完成了;用户空间要操作内核空间,需要通过系统调用;刚好,这里就有两个系统调用:copy_from_user
, copy_to_user
。
但是,Binder机制并不是这么干的。讲这么一段,是说明进程间通信并不是什么神秘的东西。那么,Binder机制是如何实现跨进程通信的呢?
Binder驱动为我们做了一切。
假设Client进程想要调用Server进程的object
对象的一个方法add
;对于这个跨进程通信过程,我们来看看Binder机制是如何做的。 (通信是一个广泛的概念,只要一个进程能调用另外一个进程里面某对象的方法,那么具体要完成什么通信内容就很容易了。)
首先,Server进程要向SM注册;告诉自己是谁,自己有什么能力;在这个场景就是Server告诉SM,它叫zhangsan
,它有一个object
对象,可以执行add
操作;于是SM建立了一张表:zhangsan
这个名字对应进程Server;
然后Client向SM查询:我需要联系一个名字叫做zhangsan
的进程里面的object
对象;这时候关键来了:进程之间通信的数据都会经过运行在内核空间里面的驱动,驱动在数据流过的时候做了一点手脚,它并不会给Client进程返回一个真正的object
对象,而是返回一个看起来跟object
一模一样的代理对象objectProxy
,这个objectProxy
也有一个add
方法,但是这个add
方法没有Server进程里面object
对象的add
方法那个能力;objectProxy
的add
只是一个傀儡,它唯一做的事情就是把参数包装然后交给驱动。(这里我们简化了SM的流程,见下文)
但是Client进程并不知道驱动返回给它的对象动过手脚,毕竟伪装的太像了,如假包换。Client开开心心地拿着objectProxy
对象然后调用add
方法;我们说过,这个add
什么也不做,直接把参数做一些包装然后直接转发给Binder驱动。
驱动收到这个消息,发现是这个objectProxy
;一查表就明白了:我之前用objectProxy
替换了object
发送给Client了,它真正应该要访问的是object
对象的add
方法;于是Binder驱动通知Server进程,调用你的object对象的add
方法,然后把结果发给我,Sever进程收到这个消息,照做之后将结果返回驱动,驱动然后把结果返回给Client
进程;于是整个过程就完成了。
由于驱动返回的objectProxy
与Server进程里面原始的object
是如此相似,给人感觉好像是直接把Server进程里面的对象object传递到了Client进程;因此,我们可以说Binder对象是可以进行跨进程传递的对象
但事实上我们知道,Binder跨进程传输并不是真的把一个对象传输到了另外一个进程;传输过程好像是Binder跨进程穿越的时候,它在一个进程留下了一个真身,在另外一个进程幻化出一个影子(这个影子可以很多个);Client进程的操作其实是对于影子的操作,影子利用Binder驱动最终让真身完成操作。
理解这一点非常重要;务必仔细体会。另外,Android系统实现这种机制使用的是代理模式, 对于Binder的访问,如果是在同一个进程(不需要跨进程),那么直接返回原始的Binder实体;如果在不同进程,那么就给他一个代理对象(影子);我们在系统源码以及AIDL的生成代码里面可以看到很多这种实现。
另外我们为了简化整个流程,隐藏了SM这一部分驱动进行的操作;实际上,由于SM与Server通常不在一个进程,Server进程向SM注册的过程也是跨进程通信,驱动也会对这个过程进行暗箱操作:SM中存在的Server端的对象实际上也是代理对象,后面Client向SM查询的时候,驱动会给Client返回另外一个代理对象。Sever进程的本地对象仅有一个,其他进程所拥有的全部都是它的代理。
一句话总结就是:Client进程只不过是持有了Server端的代理;代理对象协助驱动完成了跨进程通信。
Binder是基于C/S架构的,对于通信双方来说,发起请求的进程属于Client,接收请求的进程属于Server,由于存在进程隔离,双方不能直接通信,Binder是如何实现的呢?
写给 Android 应用工程师的 Binder 原理剖析中举的网络通信例子很贴切,Binder的通信过程与网络请求类似,网络通信过程可以简化为4个角色:Client、Server、DNS服务器和路由器。一次完整的网络通信大体过程如下:
1、Client输入Server的域名
2、DNS解析域名:
通过域名是无法直接找到相应Server的,必须先通过DNS服务器将Server的域名转化为具体的IP地址。
3、通过路由器将请求发送至Server:
Client通过DNS服务器解析到Server的IP地址后,也还不能直接向Server发起请求,需要经过路由器的层层中转才还到达Server。
4、Server返回数据:
Server接收到请求并处理后,再通过路由器将数据返回给Client。
在Binder机制中,也定义了4个角色:Client、Server、Binder驱动和ServiceManager。
具体的通信过程是这样的:
1、Server向ServiceManager注册:
Server通过Binder驱动向ServiceManager注册,声明可以对外提供服务。ServiceManager中会保留一份映射表:名字为zhangsan的Server对应的Binder引用是0x12345。/
2、Client向ServiceManager请求Server的Binder引用
Client想要请求Server的数据时,需要先通过Binder驱动向ServiceManager请求Server的Binder引用:我要向名字为zhangsan的Server通信,请告诉我Server的Binder引用。
3、向具体的Server发送请求
Client拿到这个Binder引用后,就可以通过Binder驱动和Server进行通信了。
4、Server返回结果
Server响应请求后,需要再次通过Binder驱动将结果返回给Client。
可以看到,Client、Server、ServiceManager之间的通信都是通过Binder驱动作为桥梁的,可见Binder驱动的重要性。也许你还有一点疑问,ServiceManager和Binder驱动属于两个不同的进程,它们是为Client和Server之间的进程间通信服务的,也就是说Client和Server之间的进程间通信依赖ServiceManager和Binder驱动之间的进程间通信,这就像是:“蛋生鸡,鸡生蛋,但第一个蛋得通过一只鸡孵出来”。Binder机制是如何创造第一只下蛋的鸡呢?
Android Binder设计与实现 - 设计篇中对Client、Server、Binder驱动和ServiceManager有更详细的介绍。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!