3. Binder 驱动核心数据结构详解

Binder 驱动的核心在于其精心设计的数据结构体系,它们共同构成了进程间通信的骨架。理解 binder_proc、binder_thread、binder_node、binder_ref 和 binder_buffer 这五个核心结构体,是掌握 Binder 驱动移植与适配的关键。本章将逐一剖析每个结构体的成员、作用及其在通信流程中的协作关系。

学习提示: 这五个结构体分别对应 Binder 通信中的不同角色:binder_proc 代表进程,binder_thread 代表线程,binder_node 代表服务端实体,binder_ref 代表客户端引用,binder_buffer 代表数据传输缓冲区。理解它们的关系,就理解了 Binder 的寻址与数据流转。

3.1 binder_proc:进程上下文管理器

binder_proc 是 Binder 驱动中最重要的结构体之一,它关联了一个用户空间进程与内核驱动之间的所有状态。每当一个进程通过 open() 系统调用打开 Binder 设备节点时,内核就会为该进程创建一个 binder_proc 实例。

binder_proc 结构体核心成员关系图 binder_proc 核心成员与关联结构 binder_proc 进程标识与状态 • struct task_struct *tsk • pid_t pid • unsigned int pid_hash • struct dentry *dentry • struct hlist_node proc_node • struct list_head waiting_threads • struct list_head delivered_death • int deferred_work 核心链表与红黑树 • struct rb_root nodes • struct rb_root refs_by_desc • struct rb_root refs_by_node • struct list_head threads • struct list_head todo • struct list_head delivered_bc • struct list_head nodes_by_proc • struct list_head refs_by_proc 内存与缓冲区管理 • struct vm_area_struct *vma • struct task_struct *tsk • struct list_head buffers • struct list_head free_buffers • struct rb_root free_buffers_rb • struct rb_root allocated_buffers • size_t free_async_space • struct list_head async_todo 说明:binder_proc 是 Binder 驱动的进程级上下文,管理线程、节点、引用和缓冲区四大资源。
表 3-1 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 驱动时,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 链表。

binder_thread 状态机与事务处理流程 binder_thread 状态转换与事务处理 初始状态 (looper & state) BC_ENTER_LOOPER 等待事务 (waiting_for_work) 事务到达 处理事务 (transaction_stack) 事务完成,返回等待 binder_thread 核心成员: • struct binder_proc *proc — 所属进程 • int pid — 线程 PID • struct list_head todo — 线程级待处理事务队列 • struct binder_transaction *transaction_stack — 事务栈
表 3-2 binder_thread 核心成员与功能
成员 类型 说明
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 和内部状态。

binder_node 引用计数与生命周期 binder_node 引用计数与生命周期管理 binder_node debug_id = 全局递增 引用计数 • atomic_t refcount • int internal_strong_refs • int local_weak_refs 内部状态 • int has_async_transaction • int accept_fds • int min_priority 关联信息 • struct binder_proc *proc • struct list_head refs • struct rb_node rb_node
表 3-3 binder_node 关键成员解析
成员 类型 说明
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 代理对象中存储的句柄值。

binder_ref 寻址机制与红黑树索引 binder_ref 寻址机制:描述符与红黑树索引 客户端进程 refs_by_desc 红黑树 key = desc (整数描述符) ├── desc=1 → ref_A ├── desc=2 → ref_B └── desc=3 → ref_C 用户空间通过 desc 寻址 desc=2 binder_ref desc=2, node=0x... node 指针 binder_node refs_by_node 红黑树 key = node 指针 ├── node_A → ref_A ├── node_B → ref_B └── node_C → ref_C 驱动内部通过 node 指针寻址
表 3-4 binder_ref 成员与寻址机制
成员 类型 说明
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)必须正确实现,否则会导致查找失败或死循环