3. Binder 驱动核心数据结构详解
Binder IPC 机制的实现依赖于驱动层精心设计的五个核心数据结构:binder_proc、binder_node、binder_ref、binder_buffer 和 binder_transaction。它们分别对应进程上下文、Binder 实体、Binder 引用、数据传输缓冲区以及事务描述。理解这五个结构体是掌握 Binder 驱动工作原理的基石。
drivers/android/binder.c 和 drivers/android/binder_internal.h 中定义。建议读者在学习过程中对照源码阅读,以加深理解。
3.1 binder_proc:进程上下文管理者
binder_proc 结构体用于描述一个正在使用 Binder 通信的进程上下文。每个打开 Binder 设备文件的进程在内核中都会对应一个 binder_proc 实例。它管理着该进程的所有 Binder 资源,包括线程、节点、引用、缓冲区等。
核心字段与功能
| 字段名 | 类型 | 功能描述 |
|---|---|---|
proc_node |
struct rb_node | 将当前 proc 挂入全局 proc 红黑树的节点 |
threads |
struct rb_root | 红黑树根,管理该进程内所有 binder_thread |
nodes |
struct rb_root | 红黑树根,管理该进程创建的所有 binder_node |
refs_by_desc |
struct rb_root | 红黑树根,按描述符(handle)索引 binder_ref |
refs_by_node |
struct rb_root | 红黑树根,按目标节点索引 binder_ref |
alloc |
struct binder_alloc | 内存分配器,管理进程的 Binder 缓冲区池 |
pid |
pid_t | 进程 PID |
context |
struct binder_context * | 指向 Binder 上下文(如 /dev/binder 或 /dev/hwbinder) |
todo |
struct list_head | 待处理事务链表,驱动将事务挂入此队列唤醒进程 |
binder_proc 使用红黑树(rb_root)来管理线程、节点和引用,确保查找操作的时间复杂度为 O(log n)。缓冲区管理则通过 binder_alloc 结构体实现,采用预分配和按需映射的机制。
3.2 binder_node:Binder 实体描述符
binder_node 代表一个 Binder 服务端实体。当一个进程通过 BC_ENTER_LOOPER 或 BINDER_SET_CONTEXT_MGR 注册服务时,驱动会为其创建一个 binder_node。该结构体描述了服务端提供的接口以及内核如何将事务路由到该服务。
核心字段与功能
| 字段名 | 类型 | 功能描述 |
|---|---|---|
proc |
struct binder_proc * | 指向拥有该节点的进程 |
ptr |
binder_uintptr_t | 用户空间服务端 Binder 对象的地址(弱引用) |
cookie |
binder_uintptr_t | 用户空间服务端附加数据地址 |
refs |
struct list_head | 链表头,链接所有指向该节点的 binder_ref |
internal_strong_refs |
atomic_t | 内核持有的强引用计数 |
local_weak_refs |
atomic_t | 本地弱引用计数 |
has_async_transaction |
int | 标记是否有异步事务正在处理 |
rb_node |
struct rb_node | 挂入 proc->nodes 红黑树的节点 |
ptr 和 cookie 是用户空间地址,驱动不会直接解引用,而是通过 copy_from_user / copy_to_user 进行安全拷贝。引用计数机制确保在事务处理过程中节点不会被意外释放。
3.3 binder_ref:Binder 引用与句柄映射
binder_ref 是客户端视角的 Binder 引用。每个客户端进程通过一个整型句柄(handle)来标识远程服务,驱动内部将 handle 映射为 binder_ref,再通过 binder_ref 找到目标 binder_node。
核心字段与功能
| 字段名 | 类型 | 功能描述 |
|---|---|---|
proc |
struct binder_proc * | 指向持有该引用的客户端进程 |
node |
struct binder_node * | 指向被引用的 Binder 实体节点 |
desc |
u32 | 句柄值(handle),客户端通过此值识别服务 |
rb_node_desc |
struct rb_node | 挂入 proc->refs_by_desc 红黑树的节点 |
rb_node_node |
struct rb_node | 挂入 proc->refs_by_node 红黑树的节点 |
node_entry |
struct list_head | 挂入 node->refs 链表的节点 |
death |
struct binder_ref_death * | 死亡通知回调,用于监听服务端退出 |
binder_ref 同时维护了两个红黑树索引(按 desc 和按 node),使得驱动既可以通过 handle 快速查找引用,也可以在遍历节点时快速找到所有引用该节点的客户端。这种双索引设计是 Binder 高效路由的关键。
3.4 binder_buffer:数据传输缓冲区
binder_buffer 是 Binder 驱动中用于承载跨进程数据的核心结构。每个 binder_buffer 对应一块内核分配的物理连续内存,用于在进程间传递 Parcel 数据。
核心字段与功能
| 字段名 | 类型 | 功能描述 |
|---|---|---|
data |
void * | 缓冲区在内核空间的虚拟地址 |
size |
size_t | 缓冲区总大小(字节) |
proc |
struct binder_proc * | 指向拥有该缓冲区的进程 |
free |
int | 标记缓冲区是否空闲 |
allow_user_free |
int | 标记是否允许用户空间释放此缓冲区 |
async_transaction |
int | 标记是否为异步事务分配的缓冲区 |
entry |
struct list_head | 挂入 proc->alloc.buffers 链表的节点 |
transaction |
struct binder_transaction * | 指向使用该缓冲区的事务 |
binder_alloc 分配一块连续的内核虚拟地址空间(默认 4MB)。binder_buffer 从这块空间中按需分配,使用完毕后归还。这种设计避免了频繁的页分配开销,但也要求驱动必须正确处理缓冲区生命周期,防止内存泄漏。
3.5 binder_transaction:事务描述与路由
binder_transaction 是 Binder IPC 的核心工作单元,描述了一次完整的跨进程调用。它包含了从发送方到接收方的所有上下文信息,包括数据缓冲区、目标节点、线程状态等。
核心字段与功能
| 字段名 | 类型 | 功能描述 |
|---|---|---|
work |
struct binder_work | 工作项,挂入目标线程或进程的 todo 链表 |
buffer |
struct binder_buffer * | 指向承载事务数据的缓冲区 |
from |
struct binder_thread * | 发送方线程(可能为 NULL,如来自内核) |
to_thread |
struct binder_thread * | 目标线程(如果已指定) |
to_proc |
struct binder_proc * | 目标进程 |
need_reply |
int | 标记是否需要回复(同步调用为 1,异步为 0) |
from_parent |
struct binder_transaction * | 指向父事务(用于嵌套调用场景) |
flags |
unsigned int | 事务标志(如 TF_ONE_WAY、TF_ACCEPT_FDS) |
code |
unsigned int | 事务代码,标识具体操作(如 TRANSACTION_TYPE) |
priority |
int | 事务优先级,用于继承发送方优先级 |
binder_ref,再通过 binder_ref->node 找到 binder_node,最后根据 binder_node->proc 确定目标进程。如果目标进程有空闲线程,事务直接挂入线程的 todo 链表;否则挂入进程的 todo 链表,由进程调度线程处理。