第2章 Binder驱动关键数据结构
Binder驱动运行在内核空间,是用户空间IPC请求的最终执行者。驱动维护了五类核心结构体——binder_proc、binder_node、binder_ref、binder_transaction、binder_buffer。它们构成Binder通信的底层骨架:进程上下文、实体对象、远程引用、事务记录、数据缓冲区。理解这些结构体是掌握Binder原理和AIDL性能调优的前提。
2.1 binder_proc——进程上下文
每个打开Binder设备的进程在驱动内对应一个binder_proc实例。它记录该进程的所有Binder资源:已注册的实体节点、持有的引用、待处理的事务队列以及内存映射信息。
| 字段 | 类型 | 说明 |
|---|---|---|
| tsk | task_struct * | 关联的内核进程描述符 |
| vma | struct vm_area_struct * | 用户空间映射的虚拟内存区域 |
| nodes | rb_root | 红黑树,索引该进程创建的binder_node |
| refs_by_desc | rb_root | 红黑树,按照句柄(handle)查找binder_ref |
| refs_by_node | rb_root | 红黑树,按照目标节点索引binder_ref |
| todo | struct list_head | 等待该进程处理的待办事务链表 |
| pid | pid_t | 用户空间进程ID |
2.2 binder_node——服务端实体
当服务端通过BINDER_SET_CONTEXT_MGR或AIDLaddService()注册对象时,驱动创建一个binder_node。它代表一个可被远程调用的Binder实体,内部保存对象接口方法指针、弱/强引用计数和链接到binder_proc的回指指针。
- ptr:服务端用户空间对象的虚拟地址(实际只用作唯一标识)
- cookie:注册时传入的附加数据,通常指向服务端对象本身
- proc:所属的binder_proc
- refs:所有指向该节点的引用红黑树根
- internal_strong_refs:内部强引用计数
- local_weak_refs:本地弱引用计数
2.3 binder_ref——远程引用(句柄)
客户端通过getService()获取远程Binder对象时,驱动在客户端进程的binder_proc中插入一条binder_ref,同时分配整数句柄(handle)。后续IPC调用使用句柄代替完整节点地址,提高效率。
| 字段 | 说明 |
|---|---|
| desc | 整数句柄,用户空间传递的引用标识 |
| node | 指向对应的binder_node |
| death | 死亡通知回调列表 |
2.4 binder_transaction——事务记录
一次Binder调用(包括方法参数、返回值、BR_TRANSACTION/BC_TRANSACTION命令)被封装为一个binder_transaction。事务在工作队列间传递,驱动通过它跟踪调用链、嵌套深度和返回路径。
- work:链接到proc->todo等列表
- buffer:指向binder_buffer,存储实际数据
- from:发起端binder_proc
- to_proc:目标端binder_proc
- to_thread:目标线程(可选)
- code:AIDL方法编号
- flags:一次性/异步/无返回等标记
2.5 binder_buffer——数据缓冲区
驱动通过进程的mmap共享内存(通常128KB)分配binder_buffer块。每次事务的数据空间都来自这块预映射内存,避免内核-用户空间拷贝。缓冲区被划分为多个小片,采用最佳适配算法管理。
// binder_buffer 在 binder.c 中的定义
struct binder_buffer {
struct list_head entry; // 挂在空闲链表或已分配链表
struct rb_node rb_node; // 红黑树节点(按地址排序)
unsigned free : 1; // 是否空闲
unsigned allow_user_free : 1;
unsigned async_transaction : 1;
struct binder_transaction *transaction; // 所属事务
struct binder_node *target_node;
size_t data_size; // 有效数据大小
size_t offsets_size; // 对象偏移表大小
uint8_t data[0]; // 变长数据开始
};
2.6 数据结构关系图
2.7 内核源码中的定义示例
以下摘取自Android 12内核的/drivers/staging/android/binder.c和binder_internal.h,展示了主要结构体字段的声明方式(为简洁只保留关键成员)。
// binder_internal.h
struct binder_proc {
struct hlist_node proc_node;
struct rb_root nodes;
struct rb_root refs_by_desc;
struct rb_root refs_by_node;
struct list_head waiting_threads;
struct list_head todo;
pid_t pid;
struct mm_struct *mm;
struct vm_area_struct *vma;
struct task_struct *tsk;
...
};
struct binder_node {
struct rb_node rb_node;
struct hlist_node dead_node;
struct binder_proc *proc;
uintptr_t ptr;
uintptr_t cookie;
unsigned int has_strong_ref;
unsigned int pending_strong_ref;
unsigned int has_weak_ref;
unsigned int pending_weak_ref;
int internal_strong_refs;
int local_weak_refs;
...
};
struct binder_ref {
struct rb_node rb_node_desc;
struct rb_node rb_node_node;
struct hlist_node dead_node;
struct binder_proc *proc;
struct binder_node *node;
struct binder_ref_death *death;
uint32_t desc;
...
};
struct binder_transaction {
struct binder_work work;
struct binder_thread *from;
struct binder_transaction *from_parent;
struct binder_proc *to_proc;
struct binder_thread *to_thread;
struct binder_transaction *to_parent;
struct binder_buffer *buffer;
unsigned int code;
unsigned int flags;
...
};
struct binder_buffer {
struct list_head entry;
struct rb_node rb_node;
struct binder_transaction *transaction;
struct binder_node *target_node;
size_t data_size;
size_t offsets_size;
uint8_t data[0];
};
2.8 调试与注意事项
adb shell cat /sys/kernel/debug/binder/proc/*可以查看当前各进程的节点和引用计数。
BR_FAILED_REPLY。遇到TransactionTooLargeException时优先检查事务数据大小是否超出binder_buffer的单片容量(通常1MB减去头开销)。
| 章节知识点 | 调试命令/检查点 |
|---|---|
| binder_proc: nodes/refs红黑树 | adb shell ls /sys/kernel/debug/binder/proc/<pid> |
| binder_node: 强/弱引用 | adb shell cat /sys/kernel/debug/binder/proc/<pid>/nodes |
| binder_transaction: 未完成事务 | adb shell cat /sys/kernel/debug/binder/transaction |
| binder_buffer: 共享内存使用量 | adb shell cat /proc/<pid>/maps | grep /dev/binder |
data[0]是可变长度数组,实际数据紧跟在结构体头部之后。在AIDL传递大对象(如Bitmap)时,务必注意Parcel的setDataPosition和setDataCapacity应与驱动分配的大小对齐,否则会导致binder通过计算段错误或截断。