【IPC】Binder跨进程通信机制原理

注:本文查阅网上众多博客,然后总结得出,算是学习笔记之类的,参阅博客地址见章末附录,文章未完,后续可能修改

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的桥梁,具体作用为:
  1. 传递进程间的数据:
    a.当Client向Server发起IPC请求时,Client会先将请求数据从用户空间拷贝到内核空间;
    b.数据被拷贝到内核空间后,Binder驱动将内核空间中的数据拷贝到Server用户空间中
  2. 实现线程控制:采用Binder的线程池,并由Binder驱动自身进行管理
    为了方便理解,可以将ServiceManager理解成DNS服务器,那么Binder驱动就相当于路由的功能。

3.2 Binder通信步骤

  • 注册服务
  1. Server进程向Binder驱动发起服务注册请求(申请创建一个Binder的实体),表明可以对外提供服务
  2. Binder驱动为Server创建位于内核中的Binder实体节点以及Binder引用,并通知ServiceManager注册服务(是将名字和新建的引用打包成数据包传递给ServiceManager)
  3. ServiceManager收到数据包后,从数据包中取出服务名字和引用,然后填入一张查找表中(即已注册服务)
  • 获取服务
  1. Client向Binder驱动发起获取服务的请求,传递要获取的服务名称
  2. Binder驱动将该请求转发给ServiceManager进程
  3. ServiceManager查找到Client需要的Server对应的服务信息(在查找表中查找)
  4. 通过Binder驱动将上述服务信息返回给Client进程
  • 使用服务
  1. Client进程将参数数据发送到Server进程
    a. Client进程将需要传送的数据放入到Client进程的共享内存(当前线程被挂起);
    b. Binder驱动从Client的共享内存中读取上述数据,并根据ServiceManager进程里的Server信息找到对应的Server进程
    c. Binder驱动把上述数据拷贝到Server进程的共享内存中,并通知Server进程执行解包
  2. Server进程根据Client进程需求调用目标方法
    a. 收到Binder驱动通知后,Server进程从线程池中取出线程,然后进行数据解包与调用目标方法
    b. 将最终执行结果写入到自己的共享内存中
  3. 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的引用后)

  1. 发送方会通过Binder实体请求发送操作。

  2. Binder驱动会处理这个操作请求,把发送方的数据放入写缓存(binder_write_read.write_buffer) (对于接收方为读缓冲区),并把read_size(接收方读数据)置为数据大小;

  3. 接收方之前一直在阻塞状态中,当写缓存中有数据,则会读取数据,执行命令操作

  4. 接收方执行完后,会把返回结果同样用binder_transaction_data结构体封装,写入写缓冲区(对于发送方,为读缓冲区)3

参考博客


  1. 图文详解 Android Binder跨进程通信机制 原理 ↩︎

  2. 理解 Binder 机制 ↩︎

  3. 了解Binder机制原理和底层实现 ↩︎ ↩︎

发布了139 篇原创文章 · 获赞 179 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/u013293125/article/details/105413266