Binder 内核设计深度解析手册 (复习版)

一、 核心基石:一次拷贝 (One-Copy)

Binder 相比于传统 IPC(Socket/Pipe)最显著的优势在于仅需一次数据拷贝。

1.1 内存映射机制 (mmap)

在进程初始化时,内核会为该进程分配一段虚拟地址空间,并通过 mmap 进行双重映射:

  • 内核映射区:映射到内核虚拟地址空间。
  • 用户映射区:映射到该进程的用户虚拟地址空间。
  • 物理本质:这两段虚拟地址背后对应的是同一组物理内存页

1.2 拷贝流程

  1. 发送端:调用 ioctl 发起事务,数据仍在发送端的用户空间。
  2. 内核态:内核从接收端mmap 区域中动态分配一块 binder_buffer
  3. 执行拷贝:内核调用 copy_from_user,将数据从发送端用户空间直接拷贝到接收端内核映射区
  4. 接收端直读:由于映射关系,接收端在用户态通过偏移量即可直接读取这块物理内存,无需二次拷贝。

二、 ioctl 后的内核执行链路

执行 ioctl(fd, BINDER_WRITE_READ, &bwr) 时,内核主要经历以下阶段:

2.1 写入阶段 (binder_thread_write)

  • 解析命令:识别 BC_TRANSACTIONBC_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 是进程私有的,内核必须进行“翻译”:

  1. 获取对象:根据发送方的 FD 找到内核中的 struct file
  2. 分配槽位:在接收方的文件描述符表中找一个空闲位置。
  3. 数据改写:内核在拷贝数据时,直接将数据包里的旧 FD 值修改为接收方的新 FD 值。

四、 核心数据结构关系表

结构体名称 核心职责
binder_proc 进程在内核的代理,管理 mmap 缓冲区和线程池。
binder_thread 代表具体的通信线程,维护各自的 todo 队列。
binder_node 服务端实体的内核表示,记录了用户态指针。
binder_ref 客户端对实体的引用,即我们常说的 Handle
binder_buffer mmap 区域中的动态内存块,存储传输的实际数据。

五、 限制与边界 (常见面试点)

  1. 1MB 限制:每个进程的 Binder 缓冲区默认为 1MB - 8KB
  2. 传输失败:若缓冲区耗尽或碎片化严重,会抛出 TransactionTooLargeException
  3. 大数据传输方案:对于超过 1MB 的数据,应使用 SharedMemory (ashmem) 配合 FD 传递。

 评论