Binder 内核设计深度解析手册 (复习版)
一、 核心基石:一次拷贝 (One-Copy)
Binder 相比于传统 IPC(Socket/Pipe)最显著的优势在于仅需一次数据拷贝。
1.1 内存映射机制 (mmap)
在进程初始化时,内核会为该进程分配一段虚拟地址空间,并通过 mmap 进行双重映射:
- 内核映射区:映射到内核虚拟地址空间。
- 用户映射区:映射到该进程的用户虚拟地址空间。
- 物理本质:这两段虚拟地址背后对应的是同一组物理内存页。
1.2 拷贝流程
- 发送端:调用
ioctl发起事务,数据仍在发送端的用户空间。 - 内核态:内核从接收端的
mmap区域中动态分配一块binder_buffer。 - 执行拷贝:内核调用
copy_from_user,将数据从发送端用户空间直接拷贝到接收端内核映射区。 - 接收端直读:由于映射关系,接收端在用户态通过偏移量即可直接读取这块物理内存,无需二次拷贝。
二、 ioctl 后的内核执行链路
执行 ioctl(fd, BINDER_WRITE_READ, &bwr) 时,内核主要经历以下阶段:
2.1 写入阶段 (binder_thread_write)
- 解析命令:识别
BC_TRANSACTION或BC_REPLY。 - 对象寻址:根据
handle(句柄)在当前进程的红黑树中查找binder_ref,进而定位目标的binder_node。 - 事务构建:创建
binder_transaction结构体,并关联到目标进程的todo队列。
2.2 读取阶段 (binder_thread_read)
- 阻塞等待:如果目标线程的
todo队列为空,线程进入休眠(wait_event_interruptible)。 - 任务下发:被唤醒后,内核将数据包装成
BR_TRANSACTION命令返回给用户态。
三、 高级特性解析
3.1 Oneway (异步调用)
- 立即返回:发送端拷贝完数据后,内核直接返回
BR_TRANSACTION_COMPLETE,不挂起线程。 - 串行化保障:内核利用
node->async_todo队列,确保针对同一个 Binder 对象的异步请求按序处理。 - 资源限制:异步事务占用的
mmap缓冲区被内核硬性限制在总大小的一半(约 512KB)。
3.2 FD 翻译 (File Descriptor)
由于 FD 是进程私有的,内核必须进行“翻译”:
- 获取对象:根据发送方的 FD 找到内核中的
struct file。 - 分配槽位:在接收方的文件描述符表中找一个空闲位置。
- 数据改写:内核在拷贝数据时,直接将数据包里的旧 FD 值修改为接收方的新 FD 值。
四、 核心数据结构关系表
| 结构体名称 | 核心职责 |
|---|---|
binder_proc |
进程在内核的代理,管理 mmap 缓冲区和线程池。 |
binder_thread |
代表具体的通信线程,维护各自的 todo 队列。 |
binder_node |
服务端实体的内核表示,记录了用户态指针。 |
binder_ref |
客户端对实体的引用,即我们常说的 Handle。 |
binder_buffer |
mmap 区域中的动态内存块,存储传输的实际数据。 |
五、 限制与边界 (常见面试点)
- 1MB 限制:每个进程的 Binder 缓冲区默认为
1MB - 8KB。 - 传输失败:若缓冲区耗尽或碎片化严重,会抛出
TransactionTooLargeException。 - 大数据传输方案:对于超过 1MB 的数据,应使用
SharedMemory(ashmem) 配合 FD 传递。