2. Binder驱动架构:文件结构与核心数据结构
Binder驱动是Android系统中进程间通信(IPC)的核心枢纽,它以内核模块的形式运行,向上层提供设备文件接口,向下管理进程、线程、服务节点与引用关系。本章深入剖析Binder驱动的文件结构、核心数据结构以及驱动初始化流程,帮助开发者建立对Binder内核层的完整认知。
2.1 Binder驱动的文件结构
Binder驱动在Linux内核源码中主要由两个核心文件构成:binder.c 和 binder_alloc.c。它们分工明确,共同支撑Binder IPC的完整生命周期。
| 文件 | 职责 | 关键函数/模块 |
|---|---|---|
| binder.c | Binder驱动主逻辑:设备操作、事务处理、节点管理、引用管理、线程管理 | binder_open(), binder_ioctl(), binder_thread_read(), binder_transaction() |
| binder_alloc.c | Binder内存分配器:管理进程的缓冲区池,负责内核态与用户态的数据拷贝 | binder_alloc_new_buf(), binder_alloc_free_buf(), binder_alloc_mmap_handler() |
binder.c 负责“逻辑控制”,binder_alloc.c 负责“内存搬运”。两者通过 struct binder_proc 中的 alloc 字段(类型为 struct binder_alloc)进行关联。
2.2 核心数据结构
Binder驱动围绕四个核心数据结构构建整个IPC体系:binder_proc、binder_thread、binder_node 和 binder_ref。它们分别对应进程、线程、服务节点和服务引用。
2.2.1 binder_proc —— 进程上下文
每个打开Binder设备的进程在内核中对应一个 struct binder_proc 实例,它记录了进程的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; // 红黑树:按描述符索引的binder_ref
struct rb_root refs_by_node; // 红黑树:按节点索引的binder_ref
struct list_head waiting_threads; // 等待线程链表
int pid; // 进程PID
struct binder_alloc alloc; // 内存分配器实例
// ... 其他字段
};
2.2.2 binder_thread —— 线程上下文
每个参与Binder通信的线程(包括主线程和普通线程)对应一个 struct binder_thread。它管理线程的事务栈、等待队列和当前处理的事务。
struct binder_thread {
struct binder_proc *proc; // 所属进程
struct rb_node rb_node; // 红黑树节点(挂入proc->threads)
int pid; // 线程PID
int looper; // 线程状态(BINDER_LOOPER_STATE_*)
struct binder_transaction *transaction_stack; // 事务栈
struct list_head waiting_thread_node; // 等待线程链表节点
// ... 其他字段
};
2.2.3 binder_node —— 服务节点
每个Binder服务实体在内核中对应一个 struct binder_node。它代表一个可以被其他进程引用的服务对象,包含服务的强/弱引用计数、调试ID和所属进程。
struct binder_node {
struct binder_proc *proc; // 创建该节点的进程
struct hlist_node node; // 节点链表
struct rb_node rb_node; // 红黑树节点(挂入proc->nodes)
struct list_head refs; // 指向该节点的引用链表
int internal_strong_refs; // 内部强引用计数
int local_strong_refs; // 本地强引用计数
int local_weak_refs; // 本地弱引用计数
binder_uintptr_t ptr; // 用户态服务对象地址
binder_uintptr_t cookie; // 用户态附加数据
// ... 其他字段
};
2.2.4 binder_ref —— 服务引用
当进程A需要访问进程B中的服务节点时,内核会在进程A中创建一个 struct binder_ref,作为该节点的“引用”。引用包含一个整型描述符(handle),用户态通过该描述符来标识目标服务。
struct binder_ref {
struct binder_proc *proc; // 持有该引用的进程
struct binder_node *node; // 指向的目标节点
struct rb_node rb_node_desc; // 按描述符索引的红黑树节点
struct rb_node rb_node_node; // 按节点索引的红黑树节点
int desc; // 用户态使用的描述符(handle)
int strong; // 强引用计数
int weak; // 弱引用计数
// ... 其他字段
};
binder_node 可以被多个进程引用,每个引用对应一个 binder_ref。引用中的 desc 就是用户态Binder驱动文件描述符中使用的“handle”值。
2.3 驱动初始化流程
Binder驱动的初始化分为两个阶段:模块加载阶段和设备打开阶段。模块加载阶段完成驱动注册和文件操作接口绑定;设备打开阶段完成进程上下文创建和内存映射。
2.3.1 模块加载阶段
当内核加载Binder驱动模块时,调用 binder_init() 函数,执行以下步骤:
- 注册misc设备:通过
misc_register()注册Binder为misc设备,主设备号10,次设备号动态分配。 - 设置文件操作:绑定
binder_fops结构体,包含open、release、mmap、ioctl等回调。 - 创建proc文件系统条目:在
/proc/binder/下创建状态文件,用于调试和监控。 - 初始化全局链表和锁:初始化
binder_procs全局链表、binder_dead_nodes链表以及相关自旋锁。
static int __init binder_init(void)
{
int ret;
// 1. 创建proc文件系统目录
binder_proc_dir_entry_root = proc_mkdir("binder", NULL);
binder_proc_dir_entry_proc = proc_mkdir("proc", binder_proc_dir_entry_root);
// 2. 注册misc设备
ret = misc_register(&binder_miscdev);
if (ret < 0) {
pr_err("binder: cannot register misc device\n");
return ret;
}
// 3. 初始化全局数据结构
INIT_LIST_HEAD(&binder_procs);
INIT_LIST_HEAD(&binder_dead_nodes);
spin_lock_init(&binder_lock);
hlist_add_head(&binder_debugfs_entry, ...);
pr_info("binder: Binder driver initialized\n");
return 0;
}
2.3.2 设备打开阶段
当用户态进程调用 open("/dev/binder") 时,触发 binder_open() 函数:
- 分配binder_proc:使用
kzalloc()分配并初始化struct binder_proc。 - 初始化红黑树和链表:初始化
threads、nodes、refs_by_desc、refs_by_node等红黑树根节点。 - 初始化内存分配器:调用
binder_alloc_init()初始化proc->alloc字段。 - 关联进程PID:设置
proc->pid = current->pid。 - 加入全局链表:将
proc通过proc_node挂入binder_procs全局链表。 - 创建proc文件系统条目:在
/proc/binder/proc/下创建以PID命名的调试文件。
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;
// 1. 分配binder_proc
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (!proc)
return -ENOMEM;
// 2. 初始化红黑树
proc->threads = RB_ROOT;
proc->nodes = RB_ROOT;
proc->refs_by_desc = RB_ROOT;
proc->refs_by_node = RB_ROOT;
// 3. 初始化内存分配器
binder_alloc_init(&proc->alloc);
// 4. 关联PID
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
// 5. 加入全局链表
binder_lock();
hlist_add_head(&proc->proc_node, &binder_procs);
binder_unlock();
// 6. 创建proc文件系统条目
// ...
filp->private_data = proc;
return 0;
}
binder_open() 只分配进程上下文,不分配任何缓冲区。真正的内存映射发生在后续的 mmap() 调用中,由 binder_mmap() 完成,该函数会调用 binder_alloc_mmap_handler() 分配物理页面并建立页表映射。
2.3.3 初始化流程总结
| 阶段 | 触发时机 | 关键函数 | 产出 |
|---|---|---|---|
| 模块加载 | insmod / 内核启动 | binder_init() |
misc设备注册、proc文件系统、全局链表 |
| 设备打开 | 用户态 open() | binder_open() |
binder_proc 实例、红黑树初始化 |
| 内存映射 | 用户态 mmap() | binder_mmap() |
缓冲区池、页表映射 |
| 线程注册 | 用户态 ioctl(BINDER_SET_MAX_THREADS) | binder_ioctl() |
binder_thread 实例、线程池配置 |
2.4 本章小结
- 文件结构:
binder.c负责逻辑控制,binder_alloc.c负责内存管理,两者通过binder_proc.alloc关联。 - 核心数据结构:
binder_proc(进程上下文)、binder_thread(线程上下文)、binder_node(服务节点)、binder_ref(服务引用)构成Binder IPC的四梁八柱。 - 初始化流程:模块加载 → 设备打开 → 内存映射 → 线程注册,四个阶段缺一不可。
- 调试入口:
/proc/binder/目录下的文件提供了丰富的运行时状态信息,是排查Binder问题的第一站。