Binder机制—IPC、RPC的过程

Linux内存空间与Binder Driver

Android是基于Linux操作系统的,所以需要先了解Linux内核的知识。Android进程与Linux进程一样,运行在进程固有的虚拟地址空间中。一个4GB的虚拟地址空间,3GB是用户空间,剩余的1GB是内核空间。

一个拥有独立空间的进程如何向另一个进程传递数据呢?显然要通过两个进程共享的内核空间。从内核的角度看,进程不过是一个作业单位,虽然各个进程的用户空间相对独立,但是运行在内核空间中的任务数据、代码都是彼此共享的。
image.png
Linux本身就提供IPC工具,用于两个进程通过内核进行通信。Android中的binder功能更丰富,不仅可以进行IPC通信,还可以用来调用另一个进程的函数,即支持进程之间的RPC操作。
IPC:(Inter Process Communication)跨进程通信
RPC: (Reomote Procedure Call) 远程过程调用

Binder通信的过程中,分为两个进程Client和Service。Client和Service是相对的,谁发送消息,谁就是Client,谁接收消息,谁就是Service

进程之间是如何远程调用函数的呢?

image.png
客户端要通过IPC调用实现Server端foo函数的调用,就需要将Binder IPC数据传递给Server端,传递的过程需要Binder Driver充当中间人,接收来自客户端的IPC数据,而后传递给Server端。
IPC数据包含函数调用相关的内容:服务号、RPC数据和代码、binder协议三部分构成。

  • 服务号(Handle):用来区分不同的服务,Driver层通过handle值确定将ipc数据传递给到哪个服务中,也就是目标服务的编号
  • RPC数据:用来指定指定服务中将调用的函数(包括函数名和参数)
  • Binder协议:用来表示IPC数据的处理方式(我们研究的主要就是BINDRE_WRITE_READ协议,用来传递ipc数据)

image.png
上图为binder中各种协议的作用

image.png
此图为binder数据传递的过程,这里体现了Binder机制的分层管理(服务层、RPC层、IPC层、driver层,下来还会讲解这块),binder分为Native层和Java层,每一层都有相应的分层机制。

Context Manager(ServiceMananger)进程以及注册、检索、调用的过程

在Android系统中,有个一名为Context Manager(ServiceMananger)的特殊进程,它为每个服务分配一个称为Handle的编号,并提供添加、检索等功能,Context Manager自身的的handle为0。上面说到Binder Driver会根据IPC数据中的Handle查找Service,也就是寻址的过程。

注册

为了实现这一过程,Service Server必须先把自身服务注册到Context Mananger中,在注册的过程中,Service Server会想Driver层传递IPC数据,其中就包含RPC代码(ADD_SERVICE,添加服务),RPC数据(注册服务的名称),并且目标Handle的值为0(也就是Context Manager的handle),Driver层收到IPC数据,解析之后拿到Handle=0,就会去找到ContextManager,然后将IPC数据传递给ContextManager,ContextManager收到数据后,会根据IPC数据中的服务名称,将服务注册到自身的服务列表中,并分配handle编号。

当然这个过程是一个复杂的过程,涉及到很多driver层的数据结构,当Service Server向Context Manager注册自身服务的时候,binder driver会生成一个Binder节点,用来表示Service Server中的服务,接下来会生成Binder节点的引用数据,以便ContextManager识别所生成的Binder节点,并将相关节点连接起来,根据生成的顺序,引用数据会被编号,并插入到Ipc数据中,传递给ContextManager,ContextManager会根据IPC数据中的服务名称与Binder节点编号,将服务注册到自身的服务列表中。

image.png

检索

注册完成之后,这个服务就可以被其他进程调用了,但是调用的过程又需要借助ContextManager来实现,也就是服务检索的过程,假设服务A已经注册到ContextMananger中,现在客户端B要调用A,B首先将包含 RPC代码(GET_SERVICE)、RPC数据(请求的服务名)、Handle为0的IPC数据经过Binder Driver传递给ContextManager,contextManager解析后,拿到服务的名字,在自身持有的服务列表中进行搜索,查找相应的handle编号,然后将查到的handler编号发送给Binder Driver。Binder Driver根据传递过来的服务编号查找对应的引用数据,然后在客户端B生成引用数据。

image.png

调用

最后,客户端B将接受到的引用数据编号保存到Handler中,把与服务函数相关的RPC代码、RPC数据包含进IPC数据中,经由BinderDriver发送给指定的Service Server也就是服务A,并调用A中相关的函数。

image.png

Binder Driver函数分析

首先介绍一下Driver层几个比较重要的结构体:

  • binder_proc:用来描述一个正在使用binder进程通信机制的进程。当一个进程调用open函数时,binder驱动就会为它创建一个binder_proc结构体,它保存在一个全局的hash列表中,它用来管理Binder IPC所需要的各种信息,此外它拥有Binder中其他结构体的指针。

image.png

  • binder_buffer:用来描述一个内核缓冲区,它是用来在进程之间传递数据的,每一个使用Binder通信机制的进程,在Binder驱动程序中都有一个内核缓冲区列表,用来保存驱动程序为它分配的内核缓冲区

  • binder_node:用来描述一个Binder实体对象,每一个Service组件在Driver层中都对应一个binder_node实体对象,用来描述它在内核中的状态

下面是三个非常重要的函数

– 1.binder_open

进程在使用Binder Driver时,首先要调用binder_open()函数,binder_open()与系统函数open()连接在一起,当进程调用open()函数时,Binder Driver的节点文件“dev/binder”将作为参数传给所调用的函数,Binder Driver的文件描述符作为返回值返回给进程。在调用mmap()和ioctl()时,该文件描述符将作为参数传给函数。

image.png
binder_open()函数将为打开Driver的进程生成binder_proc结构体

– 2.binder_mmap()

进程调用binder_mmap()函数在内核空间中映射出一块用来接收IPC数据的Buffer,mmap()是一个映射函数,它将用户空间的特定区域映射到内核空间的特定区域。
一般在数据从内核空间传递到用户空间时,需要调用copy_to_user,将数据复制到用户空间中,而使用binder的进程则调用mmap()函数创建与用户空间映射的Binder mmap区域

image.png

Binder Driver通过mmap()函数,根据用户进程指定的大小,在内核空间开辟一块buffer,用来接收IPC数据,也就是说A进程将数据从A的用户空间copy_from_user复制到B进程的内核空间中的buffer区域,然后B进程的buffer区域与B进程的用户空间以及物理内存进行映射,减少了一次copy的过程,这也是binder性能更优秀的原因
image.png

– 3.binder_ioctl

在控制Binder Driver时,进程通过ioctl()函数来调用binder_ioctl()函数,Driver分析随ioctl函数调用传递而来的ioctl命令,ioctl的BINDER_WRITE_READ命令用来请求Binder Driver发送或者接受IPC数据以及应答数据。

binder_ioctl负责在两个进程之间收发ipc数据以及ipc应答数据,因此,该函数必然涉及到发送数据和接收数据的两个进程,调用的过程如下:
image.png

  • (1).进程A等待接收IPC数据
  • (2).进程B调用ioctl时传入了BINDER_WRITE_READ命令,并发送ipc数据
  • (3).B进入等待,等待接收应答数据
  • (4).A收到IPC数据并将数据发送到用户空间
  • (5).A发送应答数据
  • (6).B收到应答数据并将数据发送到用户空间

ioctl(文件描述符,BINDER_WRITE_READ,&bwr)
第一个参数是调用open时拿到的文件描述符,第二个参数是命令,第三个参数是BINDER_WRITER_READ的结构体,如下:
image.png

最终通过解析bwr数据,来进行数据的传递。

注:文中的图片部分来自于《Android框架解密》–金泰延(韩),本书从几个侧面剖析了Android的整体架构,虽然版本有点旧,但是Android的整体思想是不变的,学习本书非常受益。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享