本文共 4221 字,大约阅读时间需要 14 分钟。
下面是自己分析 Android 的Binder代码时,总结出的框架,有什么不妥,敬请谅解,请以斧正!
我们讲解的思路是:让自己开发一个Binder框架,应该如何实现?
在文章中,没有谈论任何的代码,都是思路!例如,下面提到Client进程给Server进程发送 CMD_FUNC命令码的时候,在Binder驱动中,需要特别定义该命令码,例如:
#define CMD_FUNC IBinder::FIRST_CALL_TRANSACTION 1
还有BnXXX, BpXXX等 binder 类都不会讨论!
我们只是讨论 Binder 框架的设计思路,那么,有了这个思路,任何编程语言都可以实现一个Binder框架!
1 从Binder框架的思路出发
假设要我们开发一个Binder框架,我们应该怎么样实现?首先,要明确Binder框架,要实现什么功能,明确功能的定义,然后,再设计该Binder框架!
那么,现在,我们就定Binder的功能:
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++ 版本,可以手动调试,加深理解!
具体的下载地址,看我的CSDN上传的资料《binder测试 service client》
转载地址:https://mylinux.blog.csdn.net/article/details/78273281 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!