2. Binder架构总览:Client-Server模型、ServiceManager的作用、Binder驱动的核心角色
Binder IPC 的架构设计借鉴了经典的 Client-Server 分布式计算模型,但在单机环境下进行了深度优化。整个架构由三个核心角色构成:Client(客户端)、Server(服务端)和 ServiceManager(服务管理器),而 Binder 驱动 则是连接所有用户空间组件的内核级桥梁。理解这三者之间的协作关系,是掌握 Binder IPC 机制的基础。
2.1 Client-Server 通信模型
在 Binder 架构中,Server 是服务的提供者,它创建一个 Binder 实体对象(BBinder),并将其注册到 ServiceManager 中。Client 是服务的消费者,它通过服务名称从 ServiceManager 查询到 Server 的代理句柄,然后通过该句柄发起跨进程调用。这种模型将服务接口与实现分离,使得进程间通信对开发者透明。
| 角色 | 职责 | 关键对象 | 生命周期 |
|---|---|---|---|
| Client | 发起 IPC 调用,等待返回结果 | BpBinder(代理)、BpRefBase | 按需创建,调用结束后可释放 |
| Server | 实现服务接口,处理 IPC 请求 | BBinder(实体)、BnInterface | 常驻进程,随服务启动而创建 |
| ServiceManager | 服务注册、查询、权限管理 | ServiceManager 代理 | 系统启动时初始化,常驻运行 |
| Binder 驱动 | 数据传递、线程管理、引用计数 | struct binder_proc / binder_node / binder_ref | 内核模块,系统运行期间始终存在 |
2.2 ServiceManager 的作用
ServiceManager 是一个特殊的系统服务进程(servicemanager),它充当了“服务注册中心”的角色。所有 Server 在启动后,必须将自己的 Binder 实体引用注册到 ServiceManager 中,并关联一个唯一的字符串名称。Client 在需要调用某个服务时,首先向 ServiceManager 查询该名称对应的 Binder 句柄,然后才能发起通信。
ServiceManager 的核心职责可以归纳为以下几点:
- 服务注册: Server 进程通过 Binder 驱动向 ServiceManager 发送 ADD_SERVICE_TRANSACTION 命令,将自身 Binder 实体与名称绑定。
- 服务查询: Client 进程发送 CHECK_SERVICE_TRANSACTION 或 GET_SERVICE_TRANSACTION 命令,根据名称获取 Binder 句柄。
- 权限校验: ServiceManager 会检查调用方的 UID/PID,确保只有授权的进程才能注册或查询特定服务。
- 服务列表维护: 内部维护一个服务名称到 Binder 引用的映射表,支持动态添加和移除。
2.3 Binder 驱动的核心角色
Binder 驱动是 Linux 内核中的一个字符设备驱动(设备节点 /dev/binder),它负责管理所有跨进程通信的底层细节。与传统的 Socket 或 Pipe 不同,Binder 驱动不是简单的数据通道,而是一个具备状态管理能力的 IPC 中间件。
Binder 驱动在 IPC 通信中承担以下关键职责:
| 职责 | 说明 | 内核实现要点 |
|---|---|---|
| 数据传递 | 将 Client 的调用参数和 Server 的返回结果在进程间传递 | 通过 binder_ioctl 接收用户空间数据,利用 mmap 实现一次拷贝 |
| 线程管理 | 维护每个进程的线程池,将 IPC 请求分发给空闲的 Server 线程 | 使用 binder_thread 结构跟踪线程状态,支持阻塞和唤醒 |
| 引用计数 | 跟踪 Binder 实体的引用数量,在引用归零时通知 Server 释放资源 | 通过 binder_node 的 refs 字段维护强/弱引用计数 |
| 死亡通知 | 当 Server 进程异常退出时,通知所有持有其代理的 Client | 注册 binder_node 的 death 回调,在进程退出时遍历通知 |
| 权限校验 | 检查调用方的 UID/PID,确保只有授权的进程可以访问特定服务 | 在 binder_transaction 中读取 sender_euid 和 sender_pid |
mmap 在进程和内核之间共享一块内存区域。当 Client 发送数据时,驱动将数据从 Client 的用户空间拷贝到内核缓冲区;由于 Server 的地址空间已经映射到同一块内核缓冲区,因此不需要第二次拷贝,Server 可以直接读取。这就是 Binder 著名的“一次拷贝”机制,远优于传统 IPC 的两次拷贝。
2.4 三者的协作流程
一个完整的 Binder IPC 调用流程可以分为以下几个步骤:
- Server 注册: Server 进程创建 Binder 实体(BBinder),通过 Binder 驱动向 ServiceManager 发送 ADD_SERVICE 命令,将服务名称与实体句柄绑定。
- Client 查询: Client 进程通过 Binder 驱动向 ServiceManager 发送 GET_SERVICE 命令,传入服务名称,获取 Server 的 Binder 代理句柄。
- 代理创建: Client 进程根据返回的句柄,在本地创建 BpBinder 代理对象,该对象封装了与 Server 通信的所有细节。
- IPC 调用: Client 通过代理对象发起方法调用,Binder 驱动将调用参数打包成
binder_transaction,发送给 Server 进程。 - 请求处理: Server 进程的 Binder 线程池接收到请求,唤醒一个工作线程,调用对应的 BBinder 方法处理请求。
- 结果返回: Server 处理完成后,将结果通过 Binder 驱动返回给 Client,驱动唤醒等待的 Client 线程。
通过这种架构设计,Binder 实现了跨进程服务调用的位置透明性:Client 不需要知道 Server 运行在哪个进程、哪个线程,甚至不需要知道 Server 是否在同一台设备上(在 Android 多设备场景下,Binder 可以扩展为 RPC 调用)。所有复杂性都被封装在 Binder 驱动和框架层中,上层应用开发者只需关注接口定义和业务逻辑。