3. Binder 驱动核心数据结构详解
Binder 驱动的核心在于其精心设计的数据结构体系,它们共同构成了进程间通信的骨架。理解 binder_proc、binder_thread、binder_node、binder_ref 和 binder_buffer 这五个核心结构体,是掌握 Binder 驱动移植与适配的关键。本章将逐一剖析每个结构体的成员、作用及其在通信流程中的协作关系。
binder_proc 代表进程,binder_thread 代表线程,binder_node 代表服务端实体,binder_ref 代表客户端引用,binder_buffer 代表数据传输缓冲区。理解它们的关系,就理解了 Binder 的寻址与数据流转。
3.1 binder_proc:进程上下文管理器
binder_proc 是 Binder 驱动中最重要的结构体之一,它关联了一个用户空间进程与内核驱动之间的所有状态。每当一个进程通过 open() 系统调用打开 Binder 设备节点时,内核就会为该进程创建一个 binder_proc 实例。
| 分类 | 成员名称 | 类型 | 功能描述 |
|---|---|---|---|
| 进程标识 | tsk |
struct task_struct * |
指向关联的用户进程的 task_struct,用于获取进程上下文信息 |
pid |
pid_t |
进程 PID,用于驱动内部标识和调试 | |
pid_hash |
unsigned int |
PID 哈希值,用于快速查找 proc 实例 | |
| 线程管理 | threads |
struct list_head |
链表头,链接该进程下所有 binder_thread 实例 |
waiting_threads |
struct list_head |
等待处理事务的线程链表 | |
todo |
struct list_head |
进程级别的待处理事务队列 | |
| 节点与引用 | nodes |
struct rb_root |
红黑树根,管理该进程创建的 binder_node(以 node 指针为 key) |
refs_by_desc |
struct rb_root |
红黑树根,管理该进程持有的 binder_ref(以描述符为 key) | |
refs_by_node |
struct rb_root |
红黑树根,管理该进程持有的 binder_ref(以 node 指针为 key) | |
| 缓冲区管理 | vma |
struct vm_area_struct * |
用户空间映射的虚拟内存区域,用于 mmap 分配 |
buffers |
struct list_head |
所有 binder_buffer 的链表 | |
free_buffers |
struct list_head |
空闲缓冲区链表,用于快速分配 |
binder_proc 的 vma 成员与内存映射机制紧密相关。不同架构的页大小(PAGE_SIZE)和虚拟内存布局可能不同,需要确保 mmap 操作与目标平台的 struct vm_area_struct 兼容。此外,pid_hash 的计算方式在 SMP 系统上可能需要调整以避免哈希冲突。
3.2 binder_thread:线程级事务处理单元
binder_thread 代表进程中参与 Binder 通信的线程。每个线程在首次执行 ioctl(BC_ENTER_LOOPER) 或 ioctl(BC_REGISTER_LOOPER) 时,驱动会为其创建一个 binder_thread 实例,并挂入所属 binder_proc 的 threads 链表。
| 成员 | 类型 | 说明 |
|---|---|---|
proc |
struct binder_proc * |
指向所属进程的 binder_proc,用于访问进程级资源 |
pid |
int |
线程的 PID,用于标识和调试 |
looper |
int |
线程循环器状态标志(BINDER_LOOPER_STATE_*) |
todo |
struct list_head |
线程私有的待处理事务链表,优先级高于进程 todo 队列 |
transaction_stack |
struct binder_transaction * |
当前线程正在处理的事务栈,用于嵌套事务管理 |
return_error |
uint32_t |
上次操作返回的错误码,用于同步错误传递 |
waiting_for_work |
bool |
标记线程是否正在等待工作,用于调度优化 |
binder_thread 的 transaction_stack 用于处理嵌套事务(如同步调用中再次发起同步调用)。如果移植时未正确维护栈结构,可能导致死锁或事务错乱。务必确保 BC_TRANSACTION 和 BC_REPLY 的入栈/出栈操作是原子性的。
3.3 binder_node:服务端实体描述符
binder_node 是 Binder 通信中服务端实体的内核表示。当一个进程通过 BC_ATTACH_NODE 或 BC_REGISTER_LOOPER 注册一个 Binder 服务时,驱动会创建一个 binder_node 实例。该结构体包含了服务端的所有元数据,包括引用计数、调试 ID 和内部状态。
| 成员 | 类型 | 说明 |
|---|---|---|
debug_id |
int |
全局递增的调试 ID,用于日志追踪和问题定位 |
proc |
struct binder_proc * |
指向创建该节点的进程,即服务端进程 |
refs |
struct list_head |
链表头,链接所有指向该节点的 binder_ref 实例 |
internal_strong_refs |
int |
内部强引用计数,控制节点生命周期 |
local_weak_refs |
int |
本地弱引用计数,防止节点被提前释放 |
has_async_transaction |
int |
标记是否有异步事务正在处理,用于流量控制 |
accept_fds |
int |
是否允许在事务中传递文件描述符 |
min_priority |
int |
处理该节点事务的最低线程优先级 |
binder_node 的 debug_id 在调试驱动问题时非常有用。在移植时,建议保留或增强 binder_debug() 日志输出,以便在节点创建、销毁和引用计数变化时打印 debug_id。此外,min_priority 的调度策略需要与目标内核的 CFS 调度器兼容。
3.4 binder_ref:客户端引用与寻址
binder_ref 是客户端进程对服务端 binder_node 的引用。每个客户端进程在获取服务端 Binder 对象时,驱动会在该进程的 binder_proc 中创建一个 binder_ref 实例,并分配一个整数描述符(desc)供用户空间使用。这个描述符就是用户空间 Binder 代理对象中存储的句柄值。
| 成员 | 类型 | 说明 |
|---|---|---|
desc |
int |
整数描述符,用户空间通过此值引用 Binder 对象(类似文件描述符) |
node |
struct binder_node * |
指向服务端 binder_node 的指针,驱动内部通过此指针寻址 |
proc |
struct binder_proc * |
指向持有该引用的客户端进程 |
rb_node_desc |
struct rb_node |
红黑树节点,用于挂入 proc 的 refs_by_desc 树(以 desc 为 key) |
rb_node_node |
struct rb_node |
红黑树节点,用于挂入 proc 的 refs_by_node 树(以 node 指针为 key) |
death |
struct binder_ref_death * |
死亡通知回调,当服务端进程死亡时通知客户端 |
binder_ref 的双红黑树索引机制是 Binder 寻址性能的关键。用户空间通过 desc(小整数)快速查找,驱动内部通过 node 指针直接访问。在移植时,红黑树的比较函数(binder_ref_cmp 和 binder_ref_cmp_node)必须正确实现,否则会导致查找失败或死循环