Binder是Android系统进程间通信(IPC)方式之一。Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection):管道(Pipe)、信号(Signal)和跟踪(Trace)、插口(Socket)、报文队列(Message)、共享内存(Share Memory)和信号量(Semaphore)。
Binder机制在Android中的地位举足轻重,我们需要掌握的很多原理都和Binder有关:
系统中的各个进程键的通信
Android系统启动过程
AMS、PMS的原理
四大组件的原理(比如Activity的启动)
插件化原理
系统服务的Client端和Server端的通信原理(比如MediaPlayer和MeidaPlayerService)
优点
1、性能方面
在移动设备上(性能受限制的设备,比如要省电),广泛地使用跨进程通信对通信机制的性能有严格的要求,Binder相对出传统的Socket方式,更加高效。Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,共享内存方式一次内存拷贝都不需要,但实现方式又比较复杂。
2、安全方面
首先传统IPC的接收方无法获得对方进程可靠的UID和PID(用户ID进程ID),从而无法鉴别对方身份。Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志。其实Binder机制主要是在本机内进行跨进程通信,而socket等IPC方式主要是用于跨网络的通信手段,因而socket不能限制了入口和出口,所以从使用场景来看,socket给用户提供的使用范围更广,而binder由于为了保证它的数据安全性,必须限制于android系统的机子中,而且在内核中就已经对UID进行了验证。
原理
Binder框架定义了四个角色:Server,Client,ServiceManager(以后简称SMgr)以及Binder驱动。其中Server,Client,SMgr运行于用户空间,驱动运行于内核空间。这四个角色的关系和互联网类似:Server是服务器,Client是客户终端,SMgr是域名服务器(DNS),驱动是路由器。
1 Binder 驱动
和路由器一样,Binder驱动虽然默默无闻,却是通信的核心。尽管名叫‘驱动’,实际上和硬件设备没有任何关系,只是实现方式和设备驱动程序是一样的。它工作于内核态,驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。
2 ServiceManager 与实名Binder
和DNS类似,SMgr的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。注册了名字的Binder叫实名Binder,就象每个网站除了有IP地址外还有自己的网址。Server创建了Binder实体,为其取一个字符形式,可读易记的名字,将这个Binder连同名字以数据包的形式通过Binder驱动发送给SMgr,通知SMgr注册一个名叫张三的Binder,它位于某个Server中。驱动为这个穿过进程边界的Binder创建位于内核中的实体节点以及SMgr对实体的引用,将名字及新建的引用打包传递给SMgr。SMgr收数据包后,从中取出名字和引用填入一张查找表中。
细心的读者可能会发现其中的蹊跷: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。
3 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的引用。
4 匿名 Binder
并不是所有Binder都需要注册给SMgr广而告之的。Server端可以通过已经建立的Binder连接将创建的Binder实体传给Client,当然这条已经建立的Binder连接必须是通过实名Binder实现。由于这个Binder没有向SMgr注册名字,所以是个匿名Binder。Client将会收到这个匿名Binder的引用,通过这个引用向位于Server中的实体发送请求。匿名Binder为通信双方建立一条私密通道,只要Server没有把匿名Binder发给别的进程,别的进程就无法通过穷举或猜测等任何方式获得该Binder的引用,向该Binder发送请求。
Binder的线程管理
每个Binder的Server进程会创建很多线程来处理Binder请求,可以简单的理解为创建了一个Binder的线程池吧(虽然实际上并不完全是这样简单的线程管理方式),而真正管理这些线程并不是由这个Server端来管理的,而是由Binder驱动进行管理的。
一个进程的Binder线程数默认最大是16,超过的请求会被阻塞等待空闲的Binder线程。理解这一点的话,你做进程间通信时处理并发问题就会有一个底,比如使用ContentProvider时(又一个使用Binder机制的组件),你就很清楚它的CRUD(创建、检索、更新和删除)方法只能同时有16个线程在跑。
关注我获取更多知识或者投稿