注:本文查阅网上众多博客,然后总结得出,算是学习笔记之类的,参阅博客地址见章末附录,文章未完,后续可能修改
1 Binder简介
1.1 定义
定义如图所示(图片来自网上1):
2 知识储备
2.1 进程空间
一个进程空间被分为用户空间
和内核空间
。内核空间是系统内核运行的空间;用户空间是用户程序运行的空间。不同进程间的用户空间数据不可共享,不同进程间的内核空间数据是共享的;同一进程内,用户空间和内核空间都可被系统调用。示意图如下所示:
2.2 系统调用
当一个任务(进程)执行系统调用而陷入内核代码中执行时,称进程处于内核运行态(内核态);当进程在执行用户自己的代码的时候,我们称其处于用户运行态(用户态)
3 Binder跨进程通信机制模型
3.1 Binder通信模型
Binder跨进程通信机制模型基于Client-Server模式,模型原理图如下:
- ServiceManager进程:ServiceManager是一个守护进程,用于管理Service注册与查询(将字符形式的Binder名字转化为Client中对该Binder的引用)
- Binder驱动:一种虚拟设备驱动,是连接Service进程、Client进程和ServiceManager的桥梁,具体作用为:
- 传递进程间的数据:
a.当Client向Server发起IPC请求时,Client会先将请求数据从用户空间拷贝到内核空间;
b.数据被拷贝到内核空间后,Binder驱动将内核空间中的数据拷贝到Server用户空间中- 实现线程控制:采用Binder的线程池,并由Binder驱动自身进行管理
为了方便理解,可以将ServiceManager理解成DNS服务器,那么Binder驱动就相当于路由的功能。
3.2 Binder通信步骤
- 注册服务
- Server进程向Binder驱动发起服务注册请求(申请创建一个Binder的实体),表明可以对外提供服务
- Binder驱动为Server创建位于内核中的Binder实体节点以及Binder引用,并通知ServiceManager注册服务(是将名字和新建的引用打包成数据包传递给ServiceManager)
- ServiceManager收到数据包后,从数据包中取出服务名字和引用,然后填入一张查找表中(即已注册服务)
- 获取服务
- Client向Binder驱动发起获取服务的请求,传递要获取的服务名称
- Binder驱动将该请求转发给ServiceManager进程
- ServiceManager查找到Client需要的Server对应的服务信息(在查找表中查找)
- 通过Binder驱动将上述服务信息返回给Client进程
- 使用服务
- Client进程将参数数据发送到Server进程
a
. Client进程将需要传送的数据放入到Client进程的共享内存(当前线程被挂起);
b
. Binder驱动从Client的共享内存中读取上述数据,并根据ServiceManager进程里的Server信息找到对应的Server进程
c
. Binder驱动把上述数据拷贝到Server进程的共享内存中,并通知Server进程执行解包- Server进程根据Client进程需求调用目标方法
a
. 收到Binder驱动通知后,Server进程从线程池中取出线程,然后进行数据解包与调用目标方法
b
. 将最终执行结果写入到自己的共享内存中- Server进程将目标方法的结果返回给Client进程
a
. Binder驱动将Server进程的共享内存中的数据(目标方法执行结果)拷贝到Client进程的共享内存
b
. 通知Client进程获得返回结果(此时Client进程之前被挂起的线程被重新唤醒)
使用服务流程图如下所示2:
上述的Client进程、Server进程和ServiceManager进程之间的交互必须通过Binder驱动而非直接交互,原因如下:
- Client进程、Server进程和ServiceManager进程属于进程空间的用户空间,不可直接在进程间进行交互。
- Binder驱动属于进程空间的内核空间,可进行进程间和进程内交互
3.3 如何获得ServiceManager的远程接口
ServiceManager和Server都是进程,Server向SM注册Binder需要进程间通信,当前实现的是进程间通信却又用到进程间通信,这就好比鸡生蛋、蛋生鸡,但至少得先有其中之一。
巧妙的Binder解决思路:
针对Binder的通信机制,Server端拥有的是Binder的实体;Client端拥有的是Binder的引用。如果把SM看作Server端,让它在Binder驱动一运行起来时就有自己的Binder实体(代码中设置ServiceManager的Binder 其handle值恒为0)。这个Binder实体没有名字也不需要注册,所有的client都认为handle值为0的binder引用是用来与SM通信 的(代码中是这么实现的),那么这个问题就解决了。那么,Client和Server中这么达成协议了(handle值为0的引用是专门与SM通信之用 的),还不行,还需要让SM有handle值为0的实体才算大功告成。怎么实现的呢?!当一个进程调用Binder驱动时,使用 BINDER_SET_CONTEXT_MGR命令(在驱动的binder_ioctl中)将自己注册成SM时,Binder驱动会自动为它创建 Binder实体。这个Binder的引用对所有的Client都为0。3
3.4 建立C/S通道
首先要理清一个概念:client拥有自己Binder的实体,以及Server的Binder的引用;Server拥有自己Binder的实体,以及Client的Binder的引用。我们也可以从接收方和发送方的方式来理解:
-
从client向Server发数据:Client为发送方,拥有Binder的实体;Server为接收方,拥有Binder的引用
-
从server向client发数据:Server为发送方,拥有Binder的实体;client为接收方,拥有Binder的引用。
也就是说,我们在建立了C/S通路后,无需考虑谁是Client谁是Server,只要理清谁是发送方谁是接收方,就能知道Binder的实体和引用在哪边。
建立CS通路后的流程:(当接收方获得Binder的实体,发送方获得Binder的引用后)
-
发送方会通过Binder实体请求发送操作。
-
Binder驱动会处理这个操作请求,把发送方的数据放入写缓存(binder_write_read.write_buffer) (对于接收方为读缓冲区),并把read_size(接收方读数据)置为数据大小;
-
接收方之前一直在阻塞状态中,当写缓存中有数据,则会读取数据,执行命令操作
-
接收方执行完后,会把返回结果同样用binder_transaction_data结构体封装,写入写缓冲区(对于发送方,为读缓冲区)3