# 1. 基本原理
首先要明确一点 Binder 是一个 RPC(Remote Procedure Call) 框架,也就是说借助于 Binder,我们可以在 A 进程中访问 B 进程中的函数。
# 1.1 IPC 原理
RPC 一般基于 IPC(Inter-Process Communication) 来实现的,IPC 就是跨进程数据传输,大白话就是在 A 进程可以访问到 B 进程中的数据,或者说 B 进程中的数据可以传递给 A 进程,都是一个意思。
在 Linux 中,每个进程都有自己的虚拟内存地址空间。虚拟内存地址空间又分为了用户地址空间和内核地址空间。
不同进程之间用户地址空间的变量和函数是不能相互访问的。
使得 A 进程能访问到 B 进程中数据的手段我们就称之为 IPC。
虽然用户地址空间是不能互相访问的,但是不同进程的内核地址空间是映射到相同物理地址的,它们是相同和共享的,我们可以借助内核地址空间作为中转站来实现进程间数据的传输。
具体的我们在 B 进程使用 copy_from_user 将用户态数据 int a
拷贝到内核态,这样就可以在 A 进程的内核态中访问到 int a
更进一步,可以在 A 进程中调用 copy_to_user 可以将 int a
从内核地址空间拷贝到用户地址空间。至此,我们的进程 A 用户态程序就可以访问到进程 B 中的用户地址空间数据 int a
了
为了访问 int a
,需要拷贝两次数据。能不能优化一下?我们可以通过 mmap 将进程 A 的用户地址空间与内核地址空间进行映射,让他们指向相同的物理地址空间:
完成映射后,B 进程只需调用一次 copy_from_user,A 进程的用户空间中就可以访问到 int a
了。这里就优化到了一次拷贝。
# 1.2 RPC 原理
接着我们来看以下,Binder 的 RPC 是如何实现的:
一般来说,A 进程访问 B 进程函数,我们需要:
- 在 A 进程中按照固定的规则打包数据,这些数据包含了:
- 数据发给那个进程,Binder 中是一个整型变量 Handle
- 要调用目标进程中的那个函数,Binder 中用一个整型变量 Code 表示
- 目标函数的参数
- 要执行具体什么操作,也就是 Binder 协议
- 进程 B 收到数据,按照固定的格式解析出数据,调用函数,并使用相同的格式将函数的返回值传递给进程 A。
Binder 要实现的效果就是,整体上看过去,进程 A 执行进程 B 中的函数就和执行当前进程中的函数是一样的。
# 2. Binder 应用层工作流程
Binder 是一个 RPC(Remote Procedure Call) 框架,翻译成中文就是远程过程调用。也就是说通过 Binder:
- 可以在 A 进程中访问 B 进程中定义的函数
- 进程 B 中的这些等待着被远程调用的函数的集合,我们称其为 Binder 服务(Binder Service)
- 进程 A 称之为 Binder 客户端(Binder Client),进程 B 称之为 Binder 服务端(Binder Server)
- 通常,系统中的服务很多,我们需要一个管家来管理它们,服务管家(ServiceManager) 是 Android 系统启动时,启动的一个用于管理 Binder 服务(Binder Service) 的进程。通常,服务(Service) 需要事先注册到服务管家(ServiceManager),其他进程向服务管家(ServiceManager) 查询服务后才能使用服务。
- Binder 的 RPC 能力通过 Binder 驱动实现
通常一个完整的 Binder 程序涉及 4 个流程:
- 在 Binder Server 端定义好服务
- 然后向 ServiceManager 注册服务
- 在 Binder Client 中向 ServiceManager 获取到服务
- 发起远程调用,调用 Binder Server 中定义好的服务
整个流程都是建立在 Binder 驱动提供的跨进程调用能力之上:
# 关于
我叫阿豪,2015 年本科毕业于国防科学技术大学指挥信息系统专业,毕业后从事信息化装备的研发工作,主要研究方向是 Android Framework 与 Linux Kernel。
如果你对 Android Framework 感兴趣或者正在学习 Android Framework,可以关注我的微信公众号和抖音,我会持续分享我的学习经验,帮助正在学习的你少走一些弯路。学习过程中如果你有疑问或者你的经验想要分享给大家可以添加我的微信,我拉你进技术交流群。