2. Binder 架构总览:Client-Server 模型、ServiceManager 的作用、Binder 驱动在内核中的位置
Binder 是 Android 系统中实现跨进程通信(IPC)的核心机制。理解其整体架构是掌握 Binder 实战优化的前提。Binder 架构在逻辑上遵循经典的 Client-Server 通信模型,在实现上依赖一个名为 ServiceManager 的守护进程进行服务注册与查询,而底层的数据传输与线程管理则完全由内核空间的 Binder 驱动 完成。
2.1 Client-Server 通信模型
在 Binder 架构中,参与通信的进程被划分为两种角色:Client(客户端) 和 Server(服务端)。一个进程可以同时作为 Client 和 Server,但一次具体的 IPC 调用中角色是明确的。
- Server(服务端):提供特定功能的进程。它创建一个 Binder 实体对象,并将其注册到 ServiceManager 中,等待 Client 的请求。
- Client(客户端):使用服务的进程。它通过 ServiceManager 获取 Server 的 Binder 引用,然后像调用本地函数一样调用 Server 提供的方法。
- Binder 驱动:负责跨进程的数据传输、线程管理和引用计数维护。Client 和 Server 并不直接通信,所有数据都通过 Binder 驱动在内核空间进行中转和映射。
关键理解: Client 持有的 Binder 引用是一个“代理对象”(Proxy),它封装了与 Binder 驱动交互的细节。当 Client 调用代理对象的方法时,驱动会挂起 Client 线程,将调用数据拷贝到内核空间,再唤醒 Server 端的线程执行实际逻辑,最后将结果返回给 Client。
下图展示了 Client 与 Server 通过 Binder 驱动进行通信的简化流程:
2.2 ServiceManager 的作用
ServiceManager 是 Binder 架构中的“电话本”或“DNS 服务器”。它是一个独立的系统守护进程(/system/bin/servicemanager),负责管理系统中所有公开的 Binder 服务。
ServiceManager 的核心职责包括:
| 职责 | 描述 | 类比 |
|---|---|---|
| 服务注册 | Server 进程在启动后,会调用 ServiceManager.addService() 将自身 Binder 实体注册到 ServiceManager 中,并关联一个唯一的字符串名称(如 "activity"、"window")。 |
商家在工商局注册公司名称和地址。 |
| 服务查询 | Client 进程在需要某个服务时,调用 ServiceManager.getService(),传入服务名称,即可获得该服务的 Binder 引用(代理对象)。 |
消费者通过公司名称在工商局查询公司地址。 |
| 服务管理 | ServiceManager 维护一个服务列表,并监控服务进程的死亡通知。当服务进程异常退出时,ServiceManager 会清理相关记录。 | 工商局注销倒闭的公司。 |
重要特性: ServiceManager 本身也是一个 Binder 服务,它的 Binder 引用是一个“上下文对象”(Context Object),其句柄值固定为 0。这意味着任何进程都可以通过句柄 0 与 ServiceManager 通信,这是 Binder 驱动内置的特殊规则。
下图展示了 ServiceManager 在服务注册与查询过程中的桥梁作用:
2.3 Binder 驱动在内核中的位置
Binder 驱动是 Binder 架构的基石,它运行在 Linux 内核空间,是一个标准的字符设备驱动。它在内核源码树中的位置如下:
kernel/
drivers/
android/
binder.c # Binder 驱动核心实现
binder_alloc.c # Binder 内存分配器
binder_alloc.h # 内存分配器头文件
binder_trace.h # 跟踪点定义
binderfs.c # Binder 文件系统(可选)
Binder 驱动在内核中的核心功能可以归纳为以下几点:
- 设备节点管理:Binder 驱动注册为
/dev/binder设备节点。用户空间进程通过open()打开该设备,通过ioctl()进行 Binder 协议交互。 - 内存映射(mmap):Binder 驱动在内核空间分配物理内存,并通过
mmap()系统调用将同一块物理内存映射到内核空间和用户空间。这是 Binder 实现“一次拷贝”高性能通信的关键。 - 线程管理:Binder 驱动维护每个进程的线程池状态。当 Client 发起请求时,驱动会从 Server 的线程池中唤醒一个空闲线程来处理请求,实现高效的异步通信。
- 引用计数与死亡通知:驱动维护 Binder 实体的引用计数,并在实体销毁时向所有持有其引用的进程发送死亡通知。
性能关键点: 传统 IPC(如 Unix Domain Socket)需要将数据从发送方用户空间拷贝到内核空间,再从内核空间拷贝到接收方用户空间,共两次拷贝。Binder 驱动利用
mmap 技术,只需要一次拷贝(从发送方用户空间到内核空间),接收方可以直接通过映射读取数据,大幅提升了跨进程数据传输的效率。
下图展示了 Binder 驱动在内核空间中的位置及其与用户空间进程的关系:
2.4 架构总结
Binder 架构的三层角色各司其职,共同构成了 Android 跨进程通信的基石:
| 层次 | 组件 | 核心职责 | 运行空间 |
|---|---|---|---|
| 应用层 | Client / Server | 业务逻辑实现,通过 Binder 接口进行跨进程调用 | 用户空间 |
| 服务管理层 | ServiceManager | 服务注册、查询与生命周期管理 | 用户空间(守护进程) |
| 内核层 | Binder 驱动 | 数据拷贝、线程调度、内存映射、引用计数 | 内核空间 |