3. Binder驱动核心数据结构详解
Binder驱动在内核空间维护了五大数据结构——binder_proc、binder_thread、binder_node、binder_ref、binder_buffer,它们共同构成了Binder IPC的完整生命周期管理。理解这些结构体是进行多平台移植适配与参数调优的前提。
核心认知: 这五个结构体分别对应进程上下文、线程上下文、服务端实体、客户端引用、数据缓冲区。移植适配时,结构体成员的对齐方式、缓存行填充、原子操作类型是平台差异的关键点。
3.1 binder_proc —— 进程上下文
binder_proc 是Binder驱动中最重要的结构体,每个使用Binder的进程在内核中对应一个实例。它管理该进程的所有Binder资源:线程池、节点树、引用表、内存映射区等。
struct binder_proc {
struct hlist_node proc_node; // 全局proc链表节点
struct rb_root threads; // 红黑树:管理binder_thread
struct rb_root nodes; // 红黑树:管理binder_node
struct rb_root refs_by_desc; // 红黑树:按handle查找binder_ref
struct rb_root refs_by_node; // 红黑树:按node查找binder_ref
struct list_head waiting_threads; // 等待线程链表
int pid; // 进程PID
struct task_struct *tsk; // 对应task_struct
struct files_struct *files; // 文件描述符表
struct vm_area_struct *vma; // mmap映射区
struct mm_struct *vma_vm_mm; // 映射所属mm
struct binder_alloc alloc; // 内存分配器
struct list_head todo; // 待处理事务链表
struct list_head delivered_death; // 已送达死亡通知
int max_threads; // 最大线程数
int requested_threads; // 已请求线程数
int requested_threads_started; // 已启动线程数
struct dentry *debugfs_entry; // debugfs调试入口
bool oneway_spam_detection_enabled; // 单向垃圾检测开关
};
移植关注点:
struct binder_alloc alloc 成员在ARM64和x86_64平台下,其内部vma和buffer指针的地址空间布局不同,需要检查PAGE_SIZE宏定义是否与目标平台MMU页大小一致(常见4KB/16KB/64KB)。
3.2 binder_thread —— 线程上下文
每个参与Binder通信的线程在内核中对应一个binder_thread。它记录线程的当前状态、待处理事务、以及线程与进程的归属关系。
struct binder_thread {
struct binder_proc *proc; // 所属进程
struct rb_node rb_node; // 红黑树节点(挂入proc->threads)
struct list_head waiting_thread_node; // 等待线程链表节点
int pid; // 线程PID
int looper; // looper状态标志
bool looper_need_return; // 是否需要退出looper
struct binder_transaction *transaction_stack; // 事务栈
struct list_head todo; // 待处理事务链表
bool process_todo; // 是否有待处理事务
struct binder_error *return_error; // 错误返回
struct binder_error reply_error; // 回复错误
wait_queue_head_t wait; // 等待队列
struct binder_stats stats; // 统计信息
};
| 成员 | 类型 | 作用 | 移植关注点 |
|---|---|---|---|
| proc | struct binder_proc * | 指向所属进程 | 指针大小随平台变化(32位4字节/64位8字节) |
| looper | int | 线程looper状态(注册/就绪/退出) | 状态机逻辑与平台无关,但需注意原子操作 |
| transaction_stack | struct binder_transaction * | 嵌套事务栈顶指针 | 栈深度受内核栈大小限制(ARM64默认16KB) |
| wait | wait_queue_head_t | 线程阻塞等待队列 | 等待队列初始化需适配平台调度器 |
常见陷阱: 在RT-Linux或PREEMPT_RT内核上移植时,
wait_queue_head_t的spinlock实现可能不同,需要检查wait.h头文件中的锁类型定义,避免死锁。
3.3 binder_node —— 服务端实体
binder_node 代表一个Binder服务端实体(通常对应一个ServiceManager注册的服务)。它维护服务的引用计数、死亡通知回调、以及跨进程的弱/强引用管理。
struct binder_node {
struct binder_proc *proc; // 所属进程
struct rb_node rb_node; // 红黑树节点(挂入proc->nodes)
struct hlist_node dead_node; // 死亡节点链表
struct binder_work work; // 死亡通知工作项
struct rb_root refs; // 指向该node的所有ref
struct list_head async_todo; // 异步事务链表
__u32 ptr; // 用户空间服务对象地址
__u32 cookie; // 用户空间附加数据
__u32 flags; // 节点标志(如单例/允许继承)
__u8 sched_policy; // 调度策略
__u8 min_priority; // 最小优先级
bool has_async_transaction; // 是否有异步事务
int accept_fds; // 是否接受文件描述符
int inherit_rt; // 是否继承实时优先级
};
3.4 binder_ref —— 客户端引用
binder_ref 是客户端进程持有的服务端引用,通过整数句柄(handle)在用户空间标识一个服务。它维护了引用计数、死亡通知注册、以及指向对应binder_node的指针。
struct binder_ref {
struct binder_proc *proc; // 所属客户端进程
struct rb_node rb_node_desc; // 按handle排序的红黑树节点
struct rb_node rb_node_node; // 按node排序的红黑树节点
struct hlist_node node_entry; // 挂入node->refs链表
struct binder_node *node; // 指向服务端实体
struct binder_ref_death *death; // 死亡通知回调
uint32_t desc; // 用户空间handle句柄
int32_t strong; // 强引用计数
int32_t weak; // 弱引用计数
struct binder_ref_data data; // 引用数据(debug用)
};
| 成员 | 说明 | 调试参数 |
|---|---|---|
| desc | 用户空间看到的handle句柄,从0开始递增 | 通过/sys/kernel/debug/binder/proc/[pid]/refs查看 |
| strong/weak | 强/弱引用计数,控制node生命周期 | 计数异常时检查BINDER_DEBUG_REF调试开关 |
| death | 注册的死亡通知回调函数指针 | 回调函数栈大小需适配平台(建议≥4KB) |
| rb_node_desc/rb_node_node | 同时维护两棵红黑树索引 | 红黑树旋转操作在SMP平台需加锁保护 |
3.5 binder_buffer —— 数据缓冲区
binder_buffer 是Binder IPC数据传输的载体,位于进程mmap映射的物理内存中。它管理缓冲区的分配、释放、以及事务数据的序列化/反序列化。
struct binder_buffer {
struct list_head entry; // 缓冲区链表节点
struct rb_node rb_node; // 红黑树节点(按地址排序)
unsigned int free:1; // 是否空闲
unsigned int allow_user_free:1; // 是否允许用户释放
unsigned int async_transaction:1; // 是否为异步事务
unsigned int debug_id:29; // 调试ID
struct binder_transaction *transaction; // 关联的事务
struct binder_node *target_node; // 目标节点
size_t data_size; // 数据大小
size_t offsets_size; // 偏移数组大小
size_t extra_buffers_size; // 额外缓冲区大小
void *user_data; // 用户空间数据起始地址
};
内存管理关键参数:
- buffer_size:mmap映射大小,默认4MB,可通过
/sys/module/binder/parameters/binder_size调整 - max_threads:每个进程最大Binder线程数,默认15,影响并发缓冲区分配
- debug_id:29位调试ID,用于追踪缓冲区生命周期,在
dmesg中定位内存泄漏
3.6 五大数据结构关系总结
五个结构体通过指针和红黑树形成完整的引用网络,理解它们的关系是Binder驱动移植的核心基础。
| 结构体 | 所属关系 | 索引方式 | 生命周期 | 平台差异影响 |
|---|---|---|---|---|
| binder_proc | 每个进程一个 | 全局hlist | 进程打开/dev/binder到关闭 | 高 |
| binder_thread | 挂入proc->threads | 红黑树按pid | 线程注册到注销 | 中 |
| binder_node | 挂入proc->nodes | 红黑树按ptr | 服务注册到销毁 | 低 |
| binder_ref | 挂入proc->refs_by_desc/node | 双红黑树 | 获取引用到释放 | 中 |
| binder_buffer | 挂入proc->alloc | 红黑树按地址 | 分配到释放 | 高 |
移植优先级:
binder_buffer > binder_proc > binder_thread > binder_ref > binder_node。缓冲区管理涉及MMU页表操作和DMA一致性,是移植工作量最大的部分;节点和引用的红黑树操作逻辑与平台无关,只需关注指针大小和原子操作。