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)进行关联。
Binder驱动文件结构图 binder.c 设备操作层 binder_open / binder_ioctl binder_transaction binder_thread_read 节点/引用管理 binder_proc / binder_node binder_thread / binder_ref binder_alloc.c 内存分配器 binder_alloc_new_buf binder_alloc_free_buf binder_alloc_mmap_handler 缓冲区池管理 内核态↔用户态拷贝 binder_buffer 链表 struct binder_proc.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”值。
Binder核心数据结构关系图 binder_proc PID: 1234 threads (红黑树) nodes (红黑树) refs_by_desc (红黑树) refs_by_node (红黑树) alloc (binder_alloc) binder_thread PID: 1234 / TID: 5678 transaction_stack looper 状态 waiting_thread_node binder_node 服务实体 ptr / cookie 强/弱引用计数 refs 链表 proc (所属进程) rb_node binder_ref 描述符: handle = 5 node (指向目标节点) proc (持有引用的进程) 强/弱引用计数 1:N 1:N 1:N N:1

2.3 驱动初始化流程

Binder驱动的初始化分为两个阶段:模块加载阶段和设备打开阶段。模块加载阶段完成驱动注册和文件操作接口绑定;设备打开阶段完成进程上下文创建和内存映射。

2.3.1 模块加载阶段

当内核加载Binder驱动模块时,调用 binder_init() 函数,执行以下步骤:

  1. 注册misc设备:通过 misc_register() 注册Binder为misc设备,主设备号10,次设备号动态分配。
  2. 设置文件操作:绑定 binder_fops 结构体,包含 open、release、mmap、ioctl 等回调。
  3. 创建proc文件系统条目:在 /proc/binder/ 下创建状态文件,用于调试和监控。
  4. 初始化全局链表和锁:初始化 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() 函数:

  1. 分配binder_proc:使用 kzalloc() 分配并初始化 struct binder_proc。
  2. 初始化红黑树和链表:初始化 threads、nodes、refs_by_desc、refs_by_node 等红黑树根节点。
  3. 初始化内存分配器:调用 binder_alloc_init() 初始化 proc->alloc 字段。
  4. 关联进程PID:设置 proc->pid = current->pid。
  5. 加入全局链表:将 proc 通过 proc_node 挂入 binder_procs 全局链表。
  6. 创建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 实例、线程池配置
Binder驱动初始化流程 阶段1:模块加载 binder_init() misc_register + proc创建 阶段2:设备打开 binder_open() 分配binder_proc 阶段3:内存映射 binder_mmap() 分配缓冲区池 阶段4:线程注册 ioctl(BINDER_SET_MAX_THREADS) 创建binder_thread 说明:阶段1在系统启动时执行一次;阶段2~4在每个进程打开Binder设备时依次执行。 mmap() 必须在 ioctl() 之前调用,否则Binder通信无法进行。

2.4 本章小结

  • 文件结构:binder.c 负责逻辑控制,binder_alloc.c 负责内存管理,两者通过 binder_proc.alloc 关联。
  • 核心数据结构:binder_proc(进程上下文)、binder_thread(线程上下文)、binder_node(服务节点)、binder_ref(服务引用)构成Binder IPC的四梁八柱。
  • 初始化流程:模块加载 → 设备打开 → 内存映射 → 线程注册,四个阶段缺一不可。
  • 调试入口:/proc/binder/ 目录下的文件提供了丰富的运行时状态信息,是排查Binder问题的第一站。