2. Binder驱动源码结构分析
Binder驱动是Android系统中进程间通信(IPC)的核心组件,其源码位于Linux内核的drivers/android/目录下。本章将深入分析Binder驱动在内核源码中的位置、关键源文件的功能划分以及核心数据结构的设计原理,为后续的多平台移植适配奠定基础。
2.1 Binder驱动在内核源码中的位置
Binder驱动作为内核模块,其源码文件集中存放于Linux内核源码树的特定目录中。下表列出了Binder驱动相关文件在内核中的分布情况:
| 文件路径 | 说明 |
|---|---|
drivers/android/binder.c |
Binder驱动核心实现,包含设备操作、事务处理、节点管理 |
drivers/android/binder_alloc.c |
Binder内存分配器,管理进程间共享的内存缓冲区 |
drivers/android/binder_alloc.h |
内存分配器头文件,定义缓冲区管理接口 |
drivers/android/binder_internal.h |
内部数据结构定义,包含核心结构体声明 |
drivers/android/binder_trace.h |
跟踪点定义,用于调试和性能分析 |
include/uapi/linux/android/binder.h |
用户空间与内核空间的接口定义,包含ioctl命令和数据结构 |
include/uapi/linux/android/binderfs.h |
Binder文件系统接口定义 |
drivers/android/目录下的所有源文件都需要完整复制到目标内核源码树中。同时,需要确保内核配置中启用了CONFIG_ANDROID_BINDER_IPC和CONFIG_ANDROID_BINDERFS选项。
2.2 关键源文件的功能划分
Binder驱动的核心功能由两个主要源文件承担:binder.c和binder_alloc.c。它们各自负责不同的职责,形成了清晰的模块化架构。
2.2.1 binder.c — 核心控制逻辑
binder.c是Binder驱动的主文件,负责以下关键功能:
- 设备管理:实现
binder_open()、binder_release()、binder_mmap()等文件操作接口 - 事务处理:实现
binder_ioctl(),处理BINDER_WRITE_READ等核心命令 - 节点管理:维护
binder_node的创建、查找和销毁 - 引用管理:维护
binder_ref的增删改查 - 线程管理:管理
binder_thread状态和事务分发 - 死锁检测:实现循环依赖检测机制
// binder.c 核心函数示例:binder_ioctl 命令分发
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
binder_lock(__func__);
thread = binder_get_thread(proc);
// ... 根据 cmd 分发处理
switch (cmd) {
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
break;
case BINDER_SET_MAX_THREADS:
// ...
break;
// ... 其他命令
}
binder_unlock(__func__);
return ret;
}
2.2.2 binder_alloc.c — 内存分配器
binder_alloc.c专注于Binder通信所需的内存管理,其核心职责包括:
- 缓冲区分配:从预分配的虚拟地址空间中分配连续内存块
- 缓冲区释放:回收不再使用的缓冲区
- 缓冲区映射:管理用户空间与内核空间的内存映射关系
- 碎片整理:通过
binder_alloc_free_page()实现页面级回收
// binder_alloc.c 核心数据结构:缓冲区描述符
struct binder_buffer {
struct list_head entry; // 链表节点,链接到 proc->buffers
struct rb_node rb_node; // 红黑树节点,按地址排序
unsigned int free:1; // 是否空闲
unsigned int allow_user_free:1; // 是否允许用户释放
unsigned int async_transaction:1; // 是否异步事务
struct binder_transaction *transaction; // 所属事务
struct binder_node *target_node; // 目标节点
size_t data_size; // 数据大小
size_t offsets_size; // 偏移数组大小
size_t extra_buffers_size; // 额外缓冲区大小
void *data; // 数据起始地址
};
binder_alloc.c,使得在移植到不同架构时,只需调整内存分配相关的代码,而核心的事务处理逻辑保持不变。这种模块化设计显著降低了移植工作量。
2.3 核心数据结构解析
Binder驱动的运行依赖于四个核心数据结构:binder_proc、binder_node、binder_ref和binder_buffer。它们共同构成了Binder通信的骨架。
2.3.1 binder_proc — 进程上下文
binder_proc代表一个使用Binder通信的进程,是Binder驱动中最重要的数据结构之一。每个打开Binder设备的进程都对应一个binder_proc实例。
struct binder_proc {
struct hlist_node proc_node; // 全局进程哈希链表节点
struct rb_root threads; // 线程红黑树根
struct rb_root nodes; // 节点红黑树根
struct rb_root refs_by_desc; // 按描述符索引的引用红黑树
struct rb_root refs_by_node; // 按节点索引的引用红黑树
struct list_head waiting_threads; // 等待线程链表
int pid; // 进程ID
struct task_struct *tsk; // 进程任务结构体
struct files_struct *files; // 文件描述符表
struct mutex inner_lock; // 内部锁
struct binder_alloc alloc; // 内存分配器
// ... 其他字段
};
下表总结了binder_proc中关键字段的作用:
| 字段 | 类型 | 作用 |
|---|---|---|
proc_node |
hlist_node |
将进程挂入全局哈希表,用于快速查找 |
threads |
rb_root |
管理进程内的所有Binder线程 |
nodes |
rb_root |
管理进程导出的所有Binder节点 |
refs_by_desc |
rb_root |
按描述符快速查找引用 |
refs_by_node |
rb_root |
按节点快速查找引用 |
alloc |
binder_alloc |
进程私有的内存分配器实例 |
2.3.2 binder_node — 服务节点
binder_node代表一个Binder服务实体,是服务端进程导出的通信端点。每个Binder服务在驱动中对应一个binder_node。
struct binder_node {
struct hlist_node dead_node; // 死亡通知链表节点
struct rb_node rb_node; // 红黑树节点
struct hlist_node refs; // 引用链表头
int internal_strong_refs; // 内部强引用计数
int local_weak_refs; // 本地弱引用计数
unsigned int has_strong_ref:1; // 是否有强引用
unsigned int pending_strong_ref:1; // 待处理强引用
unsigned int pending_weak_ref:1; // 待处理弱引用
unsigned int has_async_transaction:1; // 是否有异步事务
unsigned int accept_fds:1; // 是否接受文件描述符
int min_priority; // 最小优先级
struct binder_proc *proc; // 所属进程
struct binder_ref __rcu *ref; // 指向引用的指针
binder_uintptr_t ptr; // 用户空间服务句柄
binder_uintptr_t cookie; // 用户空间cookie
};
binder_node中的ptr和cookie字段存储的是用户空间地址。在64位系统移植时,必须确保binder_uintptr_t类型与目标架构的指针宽度一致。32位系统使用uint32_t,64位系统使用uint64_t。
2.3.3 binder_ref — 引用描述符
binder_ref是客户端进程对服务端节点的引用,相当于一个代理对象。每个客户端通过binder_ref来访问远程服务。
struct binder_ref {
struct rb_node rb_node_desc; // 按描述符排序的红黑树节点
struct rb_node rb_node_node; // 按节点排序的红黑树节点
struct hlist_node node_entry; // 节点引用链表节点
struct binder_proc *proc; // 所属进程
struct binder_node *node; // 引用的目标节点
uint32_t desc; // 描述符(句柄)
int strong; // 强引用计数
int weak; // 弱引用计数
struct binder_ref_data data; // 引用数据
};
下表对比了binder_node和binder_ref的角色差异:
| 特性 | binder_node | binder_ref |
|---|---|---|
| 所属方 | 服务端进程 | 客户端进程 |
| 生命周期 | 服务存在期间 | 引用存在期间 |
| 唯一性 | 全局唯一 | 进程内唯一 |
| 标识方式 | 指针地址 | 描述符(句柄) |
| 主要作用 | 代表服务实体 | 代表服务引用 |
2.3.4 binder_buffer — 数据缓冲区
binder_buffer是Binder通信中实际传输数据的载体,由binder_alloc模块管理。每个事务的数据都存储在一个或多个binder_buffer中。
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; // 异步事务标志
struct binder_transaction *transaction; // 关联的事务
struct binder_node *target_node; // 目标节点
size_t data_size; // 数据区大小
size_t offsets_size; // 偏移数组大小
size_t extra_buffers_size; // 额外缓冲区大小
void *data; // 数据起始地址
};
binder_buffer的管理采用红黑树和链表相结合的方式。红黑树用于按地址快速查找和分配,链表用于遍历和回收。在移植时,如果目标架构的缓存行大小不同,可以考虑调整binder_buffer的对齐方式以提升性能。
2.4 核心数据结构关系图
下图展示了四个核心数据结构之间的关联关系,帮助理解Binder驱动的整体架构:
2.5 数据结构间的协作流程
理解四个核心数据结构如何协同工作,是掌握Binder驱动源码的关键。以下是一个典型的Binder通信流程:
- 服务注册:服务端进程通过
binder_node向驱动注册服务,驱动将节点加入binder_proc->nodes红黑树 - 获取引用:客户端进程通过服务名查询,驱动返回一个
binder_ref,其中desc字段作为句柄 - 发起事务:客户端通过
binder_ref.desc指定目标,驱动查找对应的binder_node - 分配缓冲区:驱动调用
binder_alloc分配binder_buffer,用于存储事务数据 - 数据拷贝:将用户空间数据拷贝到
binder_buffer中,完成跨进程数据传输
1. 先从
binder.h(UAPI头文件)了解用户空间接口2. 再阅读
binder_internal.h掌握内核数据结构定义3. 然后分析
binder_alloc.c理解内存管理机制4. 最后深入
binder.c掌握事务处理核心逻辑
2.6 数据结构大小与对齐要求
在进行多平台移植时,数据结构的对齐和大小是必须关注的关键点。下表列出了核心数据结构在不同架构下的典型大小:
| 数据结构 | 32位ARM (字节) | 64位ARM (字节) | 关键对齐要求 |
|---|---|---|---|
| binder_proc | 约 512 | 约 1024 | 8字节对齐 |
| binder_node | 约 128 | 约 256 | 8字节对齐 |
| binder_ref | 约 64 | 约 128 | 8字节对齐 |
| binder_buffer | 约 80 | 约 160 | 8字节对齐 |
| binder_thread | 约 96 | 约 192 | 8字节对齐 |
| binder_transaction | 约 112 | 约 224 | 8字节对齐 |
sizeof()运算的代码,特别是copy_from_user/copy_to_user中的数据大小计算。此外,struct binder_write_read等UAPI结构体中的__u32和__u64字段布局也需要重新验证。