Android Binder框架不谈代码
发布日期:2021-07-01 04:36:30 浏览次数:2 分类:技术文章

本文共 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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:二进制数据 C 代码解析数据表示
下一篇:普通程序员与优秀程序员

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2024年05月03日 22时47分16秒