下面是本身分析 Android 的Binder代码时,总结出的框架,有什么不妥,敬请谅解,请以斧正!编程
咱们讲解的思路是:让本身开发一个Binder框架,应该如何实现?数组
在文章中,没有谈论任何的代码,都是思路!例如,下面提到Client进程给Server进程发送 CMD_FUNC命令码的时候,在Binder驱动中,须要特别定义该命令码,例如:框架
#define CMD_FUNC IBinder::FIRST_CALL_TRANSACTION 1编程语言
还有BnXXX, BpXXX等 binder 类都不会讨论!函数
咱们只是讨论 Binder 框架的设计思路,那么,有了这个思路,任何编程语言均可以实现一个Binder框架!设计
1 从Binder框架的思路出发3d
假设要咱们开发一个Binder框架,咱们应该怎么样实现?首先,要明确Binder框架,要实现什么功能,明确功能的定义,而后,再设计该Binder框架!调试
那么,如今,咱们就定Binder的功能:server
1 进程间通讯;对象
2远程调用其余服务;
那么,要实现这样的功能,咱们要怎么样设计本身的 Binder框架?
下面是本身分析Android的Binder框架,给出的框架图!
设计框架的时候,咱们应该从最顶层概况,明确:
1 该框架服务哪些对象?
2 服务的对象是什么职能? 它们要完成怎么样的工做?
3 对象之间是怎么样联系?
1 总体框架图
那么,根据这些疑问,本身总结了Android的Binder框架,给出以下的框架图:
其中,能够看到,涉及到以下的对象:
1 Client进程,做为客户端,请求service服务组件;
2 service服务组件,提供服务;
3 service_manager进程,管理service服务组件;
4 Binder驱动,负责对象之间的通讯;
5 binder对象列表,是Binder驱动中,维护的一个列表,存放全部使用当前驱动进行通讯的对象;
6 service注册列表,是service_manager进程中,维护的一个列表,存放系统中,全部service组件的注册信息;
那么,咱们在这个Binder框架中,就须要涉及这样的 6个对象;那么,每一个对象之间是怎么样工做的?下面,咱们以注册一个service和请求一个service服务为例子,分析每一个对象之间的工做状态。
2 注册一个service
如今,咱们注册一个service组件,看看Binder框架中,每一个对象之间是如何卸载。假设有一个service组件,咱们给它起一个名字:abc,而后,注册到Android系统中。
有以下的框架图:
执行的流程以下:
1 server进程,建立service服务组件,把这个service服务组件的地址addr和名称“abc”发送给Binder驱动;
2 Binder驱动收到service服务组件的地址addr,封装成一个binder对象,存放在binder对象列表中。
其中,该binder对象存放在binder对象列表中,是一个数组,它所在的位置是“下标 = 1”;
因此,设置 int handle = 1; 那么,根据这个 handle = 1 的数值,做为 binder对象列表数组的下标,就能够找到 service 服务组件对应的 binder 对象;
3 Binder驱动把service服务组件存放在binder对象列表中的位置:handle = 1 和 它的名称 abc 发送给service_manager进程;
4 service_manager进程接收到 handle = 1和name = abc 这样的数据,而后,存放到service注册列表中;
5 这样,service组件的注册就完成了,有:
(1) 在 Binder驱动中,存放service组件对象的地址,构造的 binder 对象;
(2) 在service_manager进程中,存放了service组件对象在Binder驱动中存放的位置handle值和它的名称abc。
此时,咱们隐约地感受到,service_manager就是存放一个handle和name的键值对。当client想请求一个service组件的时候,须要在 service_manager中找到name对应的handle值,而后,Binder驱动根据handle值,在binder对象列表中找到service组件对应的binder对象。最终,就找到了该service组件。
3 获取一个service
通过上面的分析,咱们在系统在注册了一个service组件,名称是abc;那么,如今咱们就在一个Client进程中,获取该 abc 服务组件。
有以下的框架图:
有以下的执行流程:
1 Client向Binder请求一个service组件,该组件的名称是 abc;
因此,Client进程把组件名称 abc 发送给Binder驱动,而后,Binder把Client进程挂起等待;
2 Binder驱动把须要请求的 service名称 abc 发送给service_manager进程;
3 service_manager进程收到一个查询请求,须要查询service的名称是 abc,因此,到service注册表中查找,找到名称 abc 的元素,其 handle = 1;而后,把 handle = 1 返回给Binder驱动;
4 Binder驱动根据service_manager返回的 handle = 1 数值,做为binder对象列表数组的下标,找到对应的binder对象;而后,返回给Client进程;唤醒Client进程;
5 Client进程获得Binder驱动发送过来的binder对象引用,就是service组件的引用;那么,能够“间接”地操做service组件;
4 Client请求service服务
通过上面分析,咱们让Client进程获得了一个service组件的引用,那么,假设service组件提供一个 func() 函数,实现:
int func(int x,int y)
{
return (x + y);
}
那么,Client进程怎么样引用service服务提供的 func() 函数?
那么,能够有以下的框架图:
如今,Client想引用service提供的 func() 接口服务,就能够有以下的逻辑:
1 在client与service通讯的过程当中,咱们为 func() 函数的调用,定义一个命令码,假设为:
#define CMD_FUNC 1
那么,client就能够把 CMD_FUNC 命令码发送给service组件,当service组件接收到CMD_FUNC命令的时候,就知道是调用 func() 函数。
那么,再解出须要处理的函数x, y值,就能够丢给 func()函数处理;最后,service组件处理完 func() 函数以后以后,再把返回值传递给Client进程。
2 其实,Client和service之间进行通讯,传递的数据须要按照必定的“协议格式”来存放;这样,传递的数据语义才明确。每一个对象才能够按照协议格式,解出正确的数据。
因此,咱们能够总结以下的流程:
1 Client把CMD_FUNC这命令码和x, y参数,封装到一个数据包中,称为 pkg 数据包;
2 Client把 pkg 数据包发送给Binder驱动;
3 Binder驱动再把 pkg 数据包发送给 service 所在的server进程;
4 server进程接收到 pkg 数据包,解析数据,获得 CMD_FUNC 命令码,就指定是要调用 func() 函数;
因此,server进程再解析 x, y 参数,而后,调用 server 组件的 func() 函数来处理;
最终,获得 (x + y) 这样的结果值;
5 server 进程把 (x + y) 结果返回给Binder驱动;
6 Binder驱动再把 (x + y) 结果返回给 Client 进程;
最终,Client 进程就可以获得了server进程中,调用service组件的func()函数来处理的数据。
在开发的过程当中,Client引用service组件的func()接口服务,如同直接调用 service组件对象。其实,这是一个“远程调用”的思路。
其实,Client并无直接调用service组件,而是把须要处理的x,y 参数,传递给Binder驱动,而后,Binder驱动再转发给server进程;
最终,由server进程来处理Client进程发送过来的x, y参数。
因此,实际上,对x, y参数处理的过程,是在server进程中完成,不是在Client进程中完成。
Client进程只是把参数发送给Binder驱动,而后,Binder驱动就把Client进程挂起等待,再把数据转发给server进程处理。
当server进程处理完以后,Binder再把返回结果告诉Client进程,而后,再唤醒Client进程。
5 总结
对于Android的Binder框架来讲,它提供了一套完善的“进程通讯”机制,与普通的管道,消息队列,共享内存不同;它还提供了“远程调用”的功能。
例如,Client进程能够调用Server进程提供的service服务组件。Client使用service组件提供的接口服务时,实际上,是由Server进程使用service组件来处理数据,处理完以后,再把处理结果返回给Client进程,因此,实现一个“远程调用”的功能。
在该文件,只是提供一个大概的思路,不少细节仍是未提到!例如,Binder驱动在接收Client进程的请求时,应该阻塞挂起当前的进程,而后,把数据丢给Server进程,其中,会涉及到一个“todo”队列,而后 Binder 驱动唤醒挂载在 todo 队列上的进程,就是执行 Server 进程,处理 service 服务接口。
本身调试的一个 service, client 通讯的例子,是 C++ 版本,能够手动调试,加深理解! ---------------------