2. Binder 架构总览:驱动、ServiceManager 与 Client/Server 模型

Binder IPC 是 Android 系统中进程间通信的核心机制,其架构由三个关键组件构成:Binder 驱动(内核层)、ServiceManager(守护进程)以及 Client/Server 模型(应用层)。本章从全局视角剖析这三者的协作关系,并梳理一次完整 IPC 调用的通信流程。

2.1 架构三要素与职责划分

Binder 架构采用分层设计,将内核空间与用户空间解耦,各组件职责明确:

组件 所在层级 核心职责 关键特性
Binder 驱动 内核空间 管理 Binder 设备节点、维护线程池、处理数据拷贝与引用计数 单次拷贝、内存映射、线程管理
ServiceManager 用户空间(守护进程) 服务注册与查询、名称解析、访问控制 全局唯一、Binder 上下文管理者
Client / Server 用户空间(应用进程) 服务提供方(Server)与服务消费方(Client) 透明代理、接口定义语言(AIDL)
关键认知: Binder 驱动是唯一运行在内核态的组件,它负责将用户空间的 IPC 请求转化为内核级的操作。ServiceManager 本身也是一个 Binder Server,其 Binder 引用(句柄 0)由驱动预置,所有进程在创建时自动获得。

2.2 Binder 驱动:内核中的通信枢纽

Binder 驱动是 Linux 内核中的一个字符设备驱动(/dev/binder),它不直接参与业务逻辑,而是提供以下核心能力:

  • 内存映射(mmap): 驱动在内核空间分配一块物理内存,并通过 mmap 系统调用将其映射到用户空间。发送方将数据拷贝到内核缓冲区,接收方直接读取,实现“单次拷贝”。
  • 线程管理: 驱动维护每个进程的 Binder 线程池,当 IPC 请求到达时,驱动唤醒目标进程中空闲的 Binder 线程来处理。
  • 引用计数: 驱动跟踪每个 Binder 实体的强引用和弱引用,确保在跨进程传递 Binder 对象时不会出现野指针。
  • BCP(Binder Context Primitives): 驱动提供 BINDER_WRITE_READ ioctl 作为唯一的通信接口,所有 IPC 数据都通过该接口传递。
性能指标: 传统 Unix 域套接字(Socket)需要两次数据拷贝(用户→内核→用户),而 Binder 驱动通过内存映射将拷贝次数降为一次,在 1KB 以下小数据包场景中延迟降低约 40%。

2.3 ServiceManager:服务注册与发现中心

ServiceManager 是一个运行在独立进程中的守护进程,其 Binder 句柄固定为 0。它提供两个核心操作:

  • 服务注册(addService): Server 进程将自己的 Binder 引用(句柄)与一个字符串名称(如 "media.player")绑定,注册到 ServiceManager 中。
  • 服务查询(getService): Client 进程通过服务名称向 ServiceManager 查询,获取目标服务的 Binder 句柄,从而建立通信通道。

ServiceManager 内部维护一张 服务名称 → Binder 引用 的映射表,所有操作均通过 Binder IPC 完成,因此它本身也是一个 Binder Server。

注意: ServiceManager 的 Binder 句柄 0 是内核预置的,任何进程在初始化时都会自动获得该句柄。这意味着 ServiceManager 必须在所有 Binder 通信开始之前启动,否则 Client 无法查询到任何服务。

2.4 Client/Server 模型:透明代理与存根

在 Binder 架构中,Client 和 Server 运行在不同的进程地址空间中,它们通过 代理(Proxy) 和 存根(Stub) 实现透明通信:

  • Server 端: 实现一个 Binder 服务类(继承自 BBinder 或通过 AIDL 生成的 Stub 类),在 onTransact() 方法中处理 Client 发来的请求。
  • Client 端: 通过 ServiceManager 获取 Server 的 Binder 句柄,驱动自动在 Client 进程内创建一个 BpBinder(代理) 对象。Client 调用代理对象的方法时,驱动将请求打包并发送给 Server。
  • 数据序列化: 跨进程传递的数据必须通过 Parcel 对象进行序列化与反序列化,确保不同进程的内存布局差异被屏蔽。
设计模式: Binder 的 Proxy/Stub 模式本质上是远程代理模式(Remote Proxy)的一种实现。Client 看到的接口与本地调用完全一致,底层 IPC 细节被完全封装。

2.5 整体通信流程:一次完整的 IPC 调用

以下是一次典型 Binder IPC 调用的完整步骤,以 Client 调用 Server 的 add(int a, int b) 方法为例:

  1. 服务查询: Client 通过句柄 0 向 ServiceManager 发送 getService("calculator") 请求,ServiceManager 返回 Server 的 Binder 句柄(如 5)。
  2. 数据打包: Client 将方法名 "add" 和参数 (3, 5) 写入 Parcel 对象,调用 transact() 方法。
  3. 系统调用: transact() 触发 ioctl(fd, BINDER_WRITE_READ, &bwr),将数据传递给 Binder 驱动。
  4. 驱动处理: 驱动根据目标句柄 5 找到 Server 进程的 Binder 实体,将数据拷贝到 Server 进程的内核缓冲区,并唤醒 Server 的 Binder 线程。
  5. 数据分发: Server 的 Binder 线程从内核缓冲区读取数据,调用 onTransact() 方法,根据方法码分发到具体的业务逻辑(如 add())。
  6. 结果返回: Server 将计算结果 8 写入回复 Parcel,通过驱动原路返回给 Client。
流程要点: 整个过程中,Client 和 Server 都不需要关心对方进程的 PID 或地址空间。Binder 驱动负责路由、拷贝和线程调度,ServiceManager 负责服务发现,Client/Server 只关注业务逻辑。

2.6 Binder 架构总览 SVG 图

下图展示了 Binder 架构的完整拓扑,包含内核驱动、ServiceManager 以及 Client/Server 之间的交互关系:

Binder 架构总览图 Binder 架构总览:驱动、ServiceManager 与 Client/Server 模型 用户空间 内核空间 Client 进程 BpBinder (代理) Parcel 序列化 ioctl(BINDER_WRITE_READ) Server 进程 BBinder (存根) onTransact() 分发 业务逻辑处理 ServiceManager 句柄 0 (预置) 服务注册表 addService / getService Binder 驱动 (内核空间) /dev/binder 字符设备 内存映射 (mmap) · 单次拷贝 线程池管理 · 引用计数 BINDER_WRITE_READ ioctl 接口 IPC 请求 唤醒线程 回复数据 返回结果 getService 句柄 5 addService 说明:ServiceManager 句柄 0 由内核预置;Client 通过驱动与 Server 通信,驱动负责数据拷贝与线程调度

2.7 架构设计要点总结

设计维度 Binder 方案 传统方案(如 Socket) 优势
数据拷贝次数 1 次(mmap 共享内存) 2 次(用户→内核→用户) 延迟更低,吞吐量更高
线程模型 驱动管理线程池,按需唤醒 应用层自行管理线程 减少上下文切换
服务发现 ServiceManager 集中管理 需自行维护服务地址 解耦 Client 与 Server
安全性 驱动层校验 UID/PID 需应用层实现 内核级安全保证
接口定义 AIDL 自动生成 Proxy/Stub 手动序列化/反序列化 开发效率高,不易出错
架构启示: Binder 的设计体现了“最小特权原则”——驱动只做数据搬运和线程调度,不参与业务逻辑;ServiceManager 只做服务注册与查询,不参与具体通信。这种分层解耦使得系统易于扩展和维护。