Binder-----Android IPC(进程间通信)

http://www.cnblogs.com/linucos/archive/2012/05/24/2516623.html

http://blog.csdn.net/fuyajun01/article/details/27710535

Android系统进程间通信方式:

1.共享内存 无需拷贝,但控制复杂,难以使用。

2.管道/消息队列  存储 -转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。

3.socket      传输效率低,开销大。

4.binder     传输性能和安全性,只需复制一次。

Binder 通信过程

1.客户端将参数实例化Parcel(_data)对象,传递到Binder驱动。让客户端进程休眠,并且将传过来的pacel数据从客户端进程映射到服务端进程。

2.服务端线程调用ontransact方法解包执行相应方法操作并将返回值写入parcel(_reply)。

3.服务端线程将返回值传递给binder驱动,binder驱动重新唤醒客户端进程并把返回值传递给proxy对象,会被调用并最后被解包并作为proxy方法的返回值。

Binder 内存映射和接收缓存区管理

传统ipc 数据“发送方数据”copy到“内核空间”copy到“接收方空间”两次拷贝效率低下。

Binder由驱动负责管理数据接收缓存,驱动将发送方数据的内存映射到接收方,数据直接从发送发copy到接收方。

Binder 接收线程池管理

假如进程S是Server端,提供Binder实体,Client进程C1中通过线程T1向进程S发送请求。S为了处理这个请求需要启动线程T2,而此时线程T1处于接收返回数据的等待状态。T2处理完请求就会将处理结果返回给T1,T1 被唤醒得到处理结果。

对于Server进程S,可能会有许多Client同时发起请求,为了提高效率往往开辟线程池并发处理收到的请求. 先创建一堆线程,每个线程都用BINDER_WRITE_READ命令读Binder。这些线程会阻塞在驱动为该Binder设置的等待队列上,一旦有来 自Client的数据驱动会从队列中唤醒一个线程来处理。这样做简单直观,省去了线程池,但一开始就创建一堆线程有点浪费资源。

首先要管理线程池就要知道池子有多大,应用程序通过INDER_SET_MAX_THREADS告诉驱动最多可以创建几个线程。以后每个线程在创建,进入主 循环,退出主循环时都要分别使用BC_REGISTER_LOOP,BC_ENTER_LOOP,BC_EXIT_LOOP告知驱动,以便驱动收集和记录 当前线程池的状态。每当驱动接收完数据包返回读Binder的线程时,都要检查一下是不是已经没有闲置线程了。如果是,而且线程总数不会超出线程池最大线 程数,就会在当前读出的数据包后面再追加一条BR_SPAWN_LOOPER消息,告诉用户线程即将不够用了,请再启动一些,否则下一个请求可能不能及时 响应。新线程一启动又会通过BC_xxx_LOOP告知驱动更新状态。这样只要线程没有耗尽,总是有空闲线程在等待队列中随时待命,及时处理请求。

Binder驱动还做了一点小小的优化。当进程P1的线程T1向进程P2发送请求时,驱动会先查看一下线程T1是否也正在处理来自P2 某个线程请求但尚未完成(没有发送回复)。这种情况通常发生在两个进程都有Binder实体并互相对发时请求时。假如驱动在进程P2中发现了这样的线程, 比如说T2,就会要求T2来处理T1的这次请求。因为T2既然向T1发送了请求尚未得到返回包,说明T2肯定(或将会)阻塞在读取返回包的状态。这时候可 以让T2顺便做点事情,总比等在那里闲着好。而且如果T2不是线程池中的线程还可以为线程池分担部分工作,减少线程池使用率。

数据包接收队列与(线程)等待队列管理

数据传输的接收端有两个队列:数据包接收队列和(线程)等待队列,线程等待队列中的线程又有私有的数据包接收队列和私有的(只有自身一个线程)等待队列。

在驱动中,每个进程有一个全局的接收队列,也叫to-do队列,存放不是发往特定线程的数据包;相应地有一 个全局等待队列,所有等待从全局接收队列里收数据的线程在该队列里排队。每个线程有自己私有的to-do队列,存放发送给该线程的数据包;相应的每个线程 都有各自私有等待队列,专门用于本线程等待接收自己to-do队列里的数据。虽然名叫队列,其实线程私有等待队列中最多只有一个线程,即它自己。

驱动怎么判断哪些数据包该送入全局to-do队列,哪些数据包该送入特定线程的to-do队列呢?通常情况下发送到全局队列,上面讲的两个进程都有Binder实体并互相对发时请求时,直接放入特定线程队列中。

Binder驱动是如何递交同步交互和异步交互的。同步交互和异步交互的区别是同步交互的请求端(client)在发出请求数据包 后须要等待应答端(Server)的返回数据包,而异步交互的发送端发出请求数据包后交互即结束。

 异步交互(接口中的oneway关键字??) 驱动做了限流,令其为同步交互让路。具体做法是:对于某个Binder实 体,只要有一个异步交互没有处理完毕,例如正在被某个线程处理或还在任意一条to-do队列中排队,那么接下来发给该实体的异步交互包将不再投递到to- do队列中,而是阻塞在驱动为该实体开辟的异步交互接收队列(Binder节点的async_todo域)中,但这期间同步交互依旧不受限制直接进入 to-do队列获得处理。一直到该异步交互处理完毕下一个异步交互方可以脱离异步交互队列进入to-do队列中。之所以要这么做是因为同步交互的请求端需 要等待返回包,必须迅速处理完毕以免影响请求端的响应速度,而异步交互属于‘发射后不管’,稍微延时一点不会阻塞其它线程。所以用专门队列将过多的异步交 互暂存起来,以免突发大量异步交互挤占Server端的处理能力或耗尽线程池里的线程,进而阻塞同步交互。

总结

Binder使用Client-Server通信方式,安全性好,简单高效,再加上其面向对象的设计思想,独特的接收缓存管理和线程池管理方式,成为Android进程间通信的中流砥柱。

posted on 2015-03-18 11:38  wjw334  阅读(424)  评论(0编辑  收藏  举报

导航