Skip to content

Binder设备打开

沿 open、binder_open 和延迟释放,追踪 Binder 进程状态、context、allocator 元数据和文件生命周期。

基于android-17.0.0_r1
AndroidBinder驱动binder_proc源码阅读

Binder设备打开 ​

用户态执行 open("/dev/binder") 后,拿到的不只是一个能传给 ioctl() 的整数。内核会为这次 打开建立 binder_proc,记录打开者的进程身份、凭证、Binder context、工作队列、线程集合和 allocator 元数据,并把它挂到这个 struct file 的 private_data 上。后续 mmap、ioctl、 poll 和关闭都从这里找回同一份状态。

本文面向已经读过 Binder协议状态机 和 Binder线程状态模型 的读者,回答一个 更靠近驱动入口的问题:一次设备打开如何变成可供后续事务使用的进程级内核状态,又如何在关闭时 安全收束。

本文不展开 binder_mmap() 的页分配算法,不讲 BINDER_WRITE_READ 命令分发,也不把 binder_proc 的全部字段做成参考手册。读完后,读者应能从 ProcessState::open_driver() 走到 binder_open(),区分 binder_proc、binder_context 和 binder_alloc 的 owner,并判断 “open 成功”“mmap 完成”“资源已经释放”分别处于哪个阶段。

1. 问题边界 ​

先把三个容易混在一起的动作分开:

阶段用户态动作驱动入口主要结果
打开设备open()binder_open()创建 binder_proc 和 allocator 元数据
建立映射mmap()binder_mmap()建立 transaction address space
发送命令ioctl()binder_ioctl()创建线程状态并处理 BC/BR 协议

binder_open() 只完成第一行。它没有预先创建 Binder 线程,没有分配事务 buffer 页面,也没有把 当前进程注册成 context manager。把这三个阶段合成“打开 Binder 驱动”会掩盖真正的失败边界: 文件描述符可能已经打开,但版本协商或 mmap() 仍可能失败。

2. 设备入口 ​

2.1 用户态打开 ​

源码文件:frameworks/native/libs/binder/ProcessState.cpp

相关函数:open_driver()

cpp
static unique_fd open_driver(
        const char* driver,
        String8* error) {
    auto fd = unique_fd(
            open(driver, O_RDWR | O_CLOEXEC));
    if (!fd.ok()) {
        error->appendFormat(
                "%d (%s) Opening '%s' failed",
                errno, strerror(errno), driver);
        return {};
    }

    int vers = 0;
    int result = ioctl(
            fd.get(), BINDER_VERSION, &vers);
    if (result == -1) {
        error->appendFormat(
                "%d (%s) Binder ioctl to obtain "
                "version failed",
                errno, strerror(errno));
        return {};
    }
    if (result != 0 ||
            vers != BINDER_CURRENT_PROTOCOL_VERSION) {
        error->appendFormat(
                "Binder driver protocol(%d) does not "
                "match user space protocol(%d)! "
                "ioctl() return value: %d",
                vers,
                BINDER_CURRENT_PROTOCOL_VERSION,
                result);
        return {};
    }
    // 后续还会设置线程数和 oneway 检测。
    return fd;
}

open_driver() 的第一个系统调用才触发内核 binder_open()。返回以后,libbinder 立即查询协议 版本;因此用户态所说的“驱动可用”比内核 open() 成功更严格。若版本查询失败或不匹配,局部 unique_fd 析构并关闭 fd,刚创建的 binder_proc 随后进入释放路径。

O_CLOEXEC 约束 fd 不跨 exec 泄漏,但不改变 binder_proc 的创建逻辑。O_NONBLOCK 也不是 打开阶段的必要条件:驱动接口测试会使用它,而 ProcessState 的正常入口没有使用它。

2.2 文件操作表 ​

源码文件:kernel/common/drivers/android/binder.c

相关符号:binder_fops

c
const struct file_operations binder_fops = {
    .owner = THIS_MODULE,
    .poll = binder_poll,
    .unlocked_ioctl = binder_ioctl,
    .compat_ioctl = compat_ptr_ioctl,
    .mmap = binder_mmap,
    .open = binder_open,
    .flush = binder_flush,
    .release = binder_release,
};

Binder 设备注册时把 binder_fops 交给 misc device 或 binderfs device。VFS 打开设备后调用 .open;同一 struct file 以后再通过 .mmap、.unlocked_ioctl 和 .poll 使用 filp->private_data 中的 binder_proc。

这张表也给出了生命周期的两端:binder_open() 创建状态,binder_flush() 促使 looper 返回, binder_release() 安排最终清理。release 没有直接指向 kfree(proc),因为事务、线程和临时引用 可能仍在使用这份状态。

2.3 设备前置 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:init_binder_device()

c
static int __init init_binder_device(
        const char *name) {
    int ret;
    struct binder_device *binder_device;

    binder_device = kzalloc(
            sizeof(*binder_device), GFP_KERNEL);
    if (!binder_device)
        return -ENOMEM;

    binder_device->miscdev.fops = &binder_fops;
    binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
    binder_device->miscdev.name = name;

    refcount_set(&binder_device->ref, 1);
    binder_device->context
            .binder_context_mgr_uid = INVALID_UID;
    binder_device->context.name = name;
    mutex_init(&binder_device->context
            .context_mgr_node_lock);

    ret = misc_register(&binder_device->miscdev);
    if (ret < 0) {
        kfree(binder_device);
        return ret;
    }
    binder_add_device(binder_device);
    return ret;
}

打开之前,设备已经拥有 binder_device 和内嵌的 binder_context。因此 binder_open() 的职责 不是创建一个新的 Binder 世界,而是让本次打开加入某个已经存在的 context。context manager 节点、名称和 manager UID 属于设备 context;线程、node、ref 和工作队列属于本次打开创建的 binder_proc。

3. Proc创建 ​

3.1 内存与身份 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_open()

c
static int binder_open(
        struct inode *nodp,
        struct file *filp) {
    struct binder_proc *proc, *itr;
    struct binder_device *binder_dev;
    struct binderfs_info *info;
    struct dentry *proc_log_dir = NULL;
    bool existing_pid = false;

    proc = kzalloc(sizeof(*proc), GFP_KERNEL);
    if (proc == NULL)
        return -ENOMEM;

    dbitmap_init(&proc->dmap);
    spin_lock_init(&proc->inner_lock);
    spin_lock_init(&proc->outer_lock);
    get_task_struct(current->group_leader);
    proc->tsk = current->group_leader;
    proc->cred = get_cred(filp->f_cred);
    INIT_LIST_HEAD(&proc->todo);
    init_waitqueue_head(&proc->freeze_wait);
    // 后续继续选择 context、初始化 alloc 并登记。

当前实现直接分配 struct binder_proc。这里没有 binder_proc_wrap;若在其他内核分支看到包装 结构,必须回到对应分支重新核对,不能套用到当前源码。

kzalloc 把红黑树根、计数器和布尔状态置零,显式初始化则用于锁、链表、等待队列和 dbitmap。 proc->tsk 持有 group leader 的 task 引用,proc->pid 后面也取 group leader PID;这说明 binder_proc 表示进程级 Binder 状态,而不是触发 open() 的某一个线程。

凭证来自 filp->f_cred,并通过 get_cred() 持有引用。它是打开文件时与 file 绑定的凭证快照, 不是每次 ioctl 都从当前线程重新取一份。最终 binder_free_proc() 会分别 put_task_struct() 和 put_cred(),所有权闭环必须成对阅读。

3.2 默认优先级 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_open()、binder_supported_policy()

c
if (binder_supported_policy(current->policy)) {
    proc->default_priority.sched_policy =
            current->policy;
    proc->default_priority.prio =
            current->normal_prio;
} else {
    proc->default_priority.sched_policy =
            SCHED_NORMAL;
    proc->default_priority.prio =
            NICE_TO_PRIO(0);
}

驱动在打开时记录一个进程默认优先级。只有 Binder 支持的调度策略才原样保存,否则回落到 SCHED_NORMAL 和 nice 0 对应的内部优先级。这个值是后续事务优先级选择的输入之一,不等于 此刻创建了线程,也不保证每次事务都使用它。

3.3 初始容器 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_open()

c
binder_stats_created(BINDER_STAT_PROC);
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
INIT_LIST_HEAD(&proc->delivered_freeze);
INIT_LIST_HEAD(&proc->waiting_threads);
filp->private_data = proc;

这几条链表分别承接已投递死亡通知、已投递冻结通知和可消费进程级工作的等待线程。此时它们都 为空;线程红黑树、node 红黑树和 ref 红黑树也尚未填入元素。第一次相关 ioctl 才会逐步创建 binder_thread、binder_node 或 binder_ref。

最关键的绑定是 filp->private_data = proc。后续驱动回调不需要按 PID 猜测调用者属于哪个打开 实例,而是从当前 file 精确取回 owner。这也是“一个 PID”不能直接等价于“一个 binder_proc” 的原因。

4. Context归属 ​

相关源码:

  • kernel/common/drivers/android/binder.c
  • kernel/common/drivers/android/binder_internal.h

相关符号:binder_open()、binder_device、binder_context

c
/* binderfs stashes devices in i_private */
if (is_binderfs_device(nodp)) {
    binder_dev = nodp->i_private;
    info = nodp->i_sb->s_fs_info;
    binder_binderfs_dir_entry_proc =
            info->proc_log_dir;
} else {
    binder_dev = container_of(
            filp->private_data,
            struct binder_device,
            miscdev);
}
refcount_inc(&binder_dev->ref);
proc->context = &binder_dev->context;

驱动支持两种设备来源:

  • binderfs 设备从 inode 的 i_private 取 binder_device;
  • 传统 misc device 在进入 binder_open() 前,filp->private_data 仍指向 miscdevice,驱动用 container_of 找回外层 binder_device。

找到设备后,binder_open() 增加 device 引用并把 context 地址保存到 proc。关闭 proc 时, binder_free_proc() 再减少引用;如果设备本身已经撤销且最后一个 proc 也消失,device 才能释放。

binder_context 至少拥有 context manager 节点、manager UID、名称和保护 manager 节点的 mutex。 同一个进程若打开不同 Binder 设备,会得到指向不同 context 的 binder_proc。即使打开同一设备 多次,每次成功的 binder_open() 也会分配新的 proc;后面的同 PID 检查只决定调试文件是否重复 创建,并不把两个 proc 合并。

5. Alloc初始化 ​

源码文件:kernel/common/drivers/android/binder_alloc.c

相关函数:binder_alloc_init()、__binder_alloc_init()

c
VISIBLE_IF_KUNIT void __binder_alloc_init(
        struct binder_alloc *alloc,
        struct list_lru *freelist) {
    alloc->pid = current->group_leader->pid;
    alloc->mm = current->mm;
    mmgrab(alloc->mm);
    mutex_init(&alloc->mutex);
    INIT_LIST_HEAD(&alloc->buffers);
    alloc->freelist = freelist;
}

void binder_alloc_init(
        struct binder_alloc *alloc) {
    __binder_alloc_init(
            alloc, &binder_freelist);
}

binder_alloc 内嵌在 binder_proc 中,owner 仍是这次设备打开。初始化阶段只保存 PID、打开时的 mm_struct、allocator mutex、buffer 链表和全局 LRU;mmgrab() 保证即使进程内存上下文进入 退出流程,allocator 在清理完成前仍能安全引用 mm。

这里没有创建 pages 数组,没有建立初始 free buffer,也没有把 mapped 设为 true。这些状态 属于 binder_alloc_mmap_handler():它根据 VMA 大小建立页指针数组,创建覆盖整段映射的初始空闲 buffer,最后才用 binder_alloc_set_mapped(alloc, true) 发布“映射完成”。

这个拆分形成一个重要不变量:

text
open 成功
  ⇒ alloc 拥有 mm、mutex、buffers list 和 freelist
  ⇏ alloc 已映射
  ⇏ alloc 已拥有 transaction pages

因此只打开再关闭必须安全;驱动不能假定每个 binder_proc 都经过 mmap。

6. 全局登记 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_open()

c
mutex_lock(&binder_procs_lock);
hlist_for_each_entry(
        itr, &binder_procs, proc_node) {
    if (itr->pid == proc->pid) {
        existing_pid = true;
        break;
    }
}
hlist_add_head(&proc->proc_node, &binder_procs);
mutex_unlock(&binder_procs_lock);

binder_procs 是驱动全局观察所有打开实例的 hlist,修改由 binder_procs_lock 保护。代码先检查 是否已存在相同 group leader PID,再无条件把新的 proc 加入表头。由此可以直接排除一个常见误读: existing_pid 不是“拒绝重复打开”的条件。

它的消费者位于紧随其后的调试入口创建逻辑:

c
if (binder_debugfs_dir_entry_proc &&
        !existing_pid) {
    snprintf(strbuf, sizeof(strbuf),
            "%u", proc->pid);
    proc->debugfs_entry = debugfs_create_file(
            strbuf, 0444,
            binder_debugfs_dir_entry_proc,
            (void *)(unsigned long)proc->pid,
            &proc_fops);
}

if (binder_binderfs_dir_entry_proc &&
        !existing_pid) {
    binderfs_entry = binderfs_create_file(
            binder_binderfs_dir_entry_proc,
            strbuf, &proc_fops,
            (void *)(unsigned long)proc->pid);
    if (!IS_ERR(binderfs_entry))
        proc->binderfs_entry = binderfs_entry;
}

同 PID 的不同 context 共享按 PID 命名的 debugfs/binderfs 日志入口,打印代码会汇总该 PID 的各个 context。binderfs 日志文件创建失败只记录 warning,binder_open() 仍返回成功;调试能力不是 IPC 可用性的硬前置。

7. 关闭收束 ​

7.1 flush路径 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_flush()、binder_deferred_flush()

c
static int binder_flush(
        struct file *filp,
        fl_owner_t id) {
    struct binder_proc *proc =
            filp->private_data;

    binder_defer_work(
            proc, BINDER_DEFERRED_FLUSH);
    return 0;
}

static void binder_deferred_flush(
        struct binder_proc *proc) {
    struct rb_node *n;

    binder_inner_proc_lock(proc);
    for (n = rb_first(&proc->threads);
            n != NULL; n = rb_next(n)) {
        struct binder_thread *thread =
                rb_entry(n, struct binder_thread,
                        rb_node);

        thread->looper_need_return = true;
        if (thread->looper &
                BINDER_LOOPER_STATE_WAITING)
            wake_up_interruptible(&thread->wait);
    }
    binder_inner_proc_unlock(proc);
}

flush 不释放 proc。它在 worker 中遍历线程,把 looper_need_return 置为 true,并唤醒正在等待的 looper。消费者是 binder_has_work_ilocked() 和读循环:线程醒来后观察该标志,从驱动返回用户态。

这条路径解决的是“不要让关闭中的进程仍永久睡在 Binder wait queue”,而不是“立即销毁全部 对象”。把它和 release 分开,能够避免一边持有 VFS 关闭上下文,一边执行大规模线程、node、ref 和 transaction 清理。

7.2 release路径 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_release()、binder_defer_work()

c
static int binder_release(
        struct inode *nodp,
        struct file *filp) {
    struct binder_proc *proc =
            filp->private_data;

    debugfs_remove(proc->debugfs_entry);
    if (proc->binderfs_entry) {
        simple_recursive_removal(
                proc->binderfs_entry, NULL);
        proc->binderfs_entry = NULL;
    }

    binder_defer_work(
            proc, BINDER_DEFERRED_RELEASE);
    return 0;
}

static void binder_defer_work(
        struct binder_proc *proc,
        enum binder_deferred_state defer) {
    guard(mutex)(&binder_deferred_lock);
    proc->deferred_work |= defer;
    if (hlist_unhashed(
            &proc->deferred_work_node)) {
        hlist_add_head(
                &proc->deferred_work_node,
                &binder_deferred_list);
        schedule_work(&binder_deferred_work);
    }
}

release 先撤掉这个 proc 自己拥有的调试入口,再把 RELEASE 位合并进 proc->deferred_work。同一个 proc 的 FLUSH 和 RELEASE 可以合并在一项 deferred work 中;worker 按 flush 在前、release 在后的 顺序处理,避免先释放 proc 再访问线程集合。

7.3 延迟释放 ​

相关源码:

  • kernel/common/drivers/android/binder.c
  • kernel/common/drivers/android/binder_alloc.c

相关函数:binder_deferred_release()、binder_free_proc()、 binder_alloc_deferred_release()

binder_deferred_release() 先把 proc 从全局 binder_procs 删除,再在 context 锁下撤销可能由它 拥有的 context manager 节点。随后它标记 is_dead,逐项收束:

  1. 从 proc->threads 移除线程并处理仍活动的 transaction stack;
  2. 从 proc->nodes 移除本地 node,必要时向远端 ref 投递死亡通知;
  3. 清理 refs_by_desc 中的远端引用;
  4. 释放 proc todo、已投递死亡通知和冻结通知;
  5. 通过 tmp_ref 判断是否已经无人再使用 proc。

真正释放 proc 的条件位于 binder_proc_dec_tmpref():

c
static void binder_proc_dec_tmpref(
        struct binder_proc *proc) {
    binder_inner_proc_lock(proc);
    proc->tmp_ref--;
    if (proc->is_dead &&
            RB_EMPTY_ROOT(&proc->threads) &&
            !proc->tmp_ref) {
        binder_inner_proc_unlock(proc);
        binder_free_proc(proc);
        return;
    }
    binder_inner_proc_unlock(proc);
}

所以 fd 关闭不是 binder_proc 立即 kfree 的同义词。只要清理中的线程或事务仍持有临时引用, proc 就继续存活;最后一个临时使用者归还引用时才触发 binder_free_proc()。

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_free_proc()

c
static void binder_free_proc(
        struct binder_proc *proc) {
    struct binder_device *device;

    BUG_ON(!list_empty(&proc->todo));
    BUG_ON(!list_empty(&proc->delivered_death));
    device = container_of(
            proc->context,
            struct binder_device, context);
    if (refcount_dec_and_test(&device->ref)) {
        binder_remove_device(device);
        kfree(proc->context->name);
        kfree(device);
    }
    binder_alloc_deferred_release(&proc->alloc);
    put_task_struct(proc->tsk);
    put_cred(proc->cred);
    binder_stats_deleted(BINDER_STAT_PROC);
    dbitmap_free(&proc->dmap);
    kfree(proc);
}

最终释放顺序与打开时的所有权一一对应:device ref、allocator、task ref、cred ref、统计、dbitmap 和 proc 本体都在这里归还。BUG_ON 还表达了释放前不变量:proc todo 和已投递死亡通知必须已经 清空,不能靠 kfree 隐式丢弃。

8. 映射边界 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_mmap()、binder_vma_close()

c
static int binder_mmap(
        struct file *filp,
        struct vm_area_struct *vma) {
    struct binder_proc *proc =
            filp->private_data;

    if (proc->tsk != current->group_leader)
        return -EINVAL;

    vm_flags_mod(vma,
            VM_DONTCOPY | VM_MIXEDMAP,
            VM_MAYWRITE);
    vma->vm_ops = &binder_vm_ops;
    vma->vm_private_data = proc;
    return binder_alloc_mmap_handler(
            &proc->alloc, vma);
}

static void binder_vma_close(
        struct vm_area_struct *vma) {
    struct binder_proc *proc =
            vma->vm_private_data;
    binder_alloc_vma_close(&proc->alloc);
}

映射只能由打开时记录的同一个 group leader 建立,否则返回 -EINVAL。VMA 再次把 proc 保存到 vm_private_data,关闭映射时把 allocator 的 mapped 状态清掉,阻止新的 incoming transaction 继续分配 buffer。

allocator 最终释放还有一道硬约束:

c
void binder_alloc_deferred_release(
        struct binder_alloc *alloc) {
    mutex_lock(&alloc->mutex);
    BUG_ON(alloc->mapped);

    while ((n = rb_first(
            &alloc->allocated_buffers))) {
        buffer = rb_entry(
                n, struct binder_buffer, rb_node);
        BUG_ON(buffer->transaction);
        binder_free_buf_locked(alloc, buffer);
    }
    // 继续释放 buffer 描述、安装页面和 LRU 状态。
    mutex_unlock(&alloc->mutex);
    kvfree(alloc->pages);
    if (alloc->mm)
        mmdrop(alloc->mm);
}

BUG_ON(alloc->mapped) 要求 VMA 生命周期先收束,BUG_ON(buffer->transaction) 要求活动事务先 解除。完整顺序不是简单的 close(fd) → free pages,而是文件、VMA、事务和临时引用共同满足清理 条件后,allocator 才释放页面数组并归还 mmgrab() 对应的 mm 引用。

9. 测试输入 ​

9.1 OpenNoMmap ​

源码文件:frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp

相关测试:BinderDriverInterfaceTest.OpenNoMmap

cpp
TEST_F(BinderDriverInterfaceTest, OpenNoMmap) {
    int binderFd = open(
            BINDER_DEV_NAME,
            O_RDWR | O_NONBLOCK | O_CLOEXEC);
    ASSERT_GE(binderFd, 0);
    close(binderFd);
}

测试输入是一次没有后续 mmap 的独立设备打开。关键断言只有 fd >= 0,随后立即关闭。它验证了 两个边界:binder_open() 不依赖 transaction mapping 才能成功;从未映射的 allocator 也必须能 通过关闭路径安全清理。

它没有查询协议版本,没有发送 ioctl,也没有断言 debugfs 内容,因此不能用来证明完整 libbinder 初始化成功、事务可收发或调试节点一定创建。

9.2 Alloc KUnit ​

源码文件:kernel/common/drivers/android/tests/binder_alloc_kunit.c

相关测试:binder_alloc_test_init_freelist()、binder_alloc_test_mmap()、 binder_alloc_test_exit()

c
static void binder_alloc_test_init_freelist(
        struct kunit *test) {
    struct binder_alloc_test *priv = test->priv;

    KUNIT_EXPECT_PTR_EQ(
            test, priv->alloc.freelist,
            &priv->binder_test_freelist);
}

static void binder_alloc_test_mmap(
        struct kunit *test) {
    struct binder_alloc_test *priv = test->priv;
    struct binder_alloc *alloc =
            &priv->alloc;

    KUNIT_EXPECT_EQ(test, alloc->mapped, true);
    KUNIT_EXPECT_EQ(
            test, alloc->buffer_size,
            BINDER_MMAP_SIZE);
    KUNIT_EXPECT_PTR_EQ(
            test,
            rb_first(&alloc->allocated_buffers),
            NULL);
}

fixture 先附着一个 mm,再调用 __binder_alloc_init(),随后通过测试 file 建立映射。第一个测试断言 初始化选择了 fixture 的 LRU;第二个测试断言 mmap 以后 mapped 为 true、映射大小正确、尚无已分配 事务 buffer,并检查整段空间形成一个 free buffer。

teardown 关闭 backing file,调用 binder_alloc_deferred_release(),最后断言测试 LRU 为空。它验证 allocator 的 init、mmap、VMA close 和 deferred release 可以形成闭环,但 fixture 没有创建完整 binder_proc,也没有覆盖 node、ref、死亡通知或活动事务清理。

10. 失败路径 ​

binder_open() 自身的硬失败点很少,但边界非常明确:

位置结果已建立状态恢复方式
kzalloc(proc) 失败-ENOMEM无 proc、无引用用户态收到 open 失败,可在资源恢复后重试
debugfs 不可用不影响返回值proc 已登记IPC 继续工作,仅缺少该入口
binderfs 日志创建失败warning,仍返回 0proc 已登记IPC 继续工作,可查挂载与权限
BINDER_VERSION 失败open 已成功,libbinder 判失败proc 等待 fd 关闭清理检查驱动/UAPI 匹配
mmap 失败open 已成功,ProcessState 关闭 fdproc 和 alloc 元数据已存在检查 VMA 条件与内存失败点

binder_alloc_init() 没有错误返回;它只获取 mm 引用和初始化元数据。真正可能返回 -EINVAL、 -EPERM、-EBUSY 或 -ENOMEM 的地址空间条件位于 mmap 阶段,应在映射专题中继续展开。

打开函数本身也没有“取消后恢复”的异步状态机:它在当前系统调用内完成或返回 -ENOMEM。与取消 最接近的动作是上层在版本或 mmap 失败后关闭 fd;恢复不是复用已经 release 的 proc,而是重新执行 一次 open,建立新的 file 和新的 binder_proc。

11. 源码复现 ​

打开到可用 ​

这张图只覆盖设备打开与可用前置;真正的 VMA 映射、页安装和 ioctl 事务分别由 BD014~BD017 负责, 不要在本篇把 fd 返回误读为服务已经能收发事务。

在对应源码 checkout 中,可以用以下只读命令重走本文主线:

bash
rg -n "open_driver|ProcessState::ProcessState" \
  frameworks/native/libs/binder/ProcessState.cpp

rg -n "binder_fops|binder_open|binder_flush|binder_release" \
  kernel/common/drivers/android/binder.c

rg -n "binder_alloc_init|binder_alloc_deferred_release" \
  kernel/common/drivers/android/binder_alloc.c

rg -n "OpenNoMmap|binder_alloc_test_mmap" \
  frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp \
  kernel/common/drivers/android/tests/binder_alloc_kunit.c

阅读时尝试回答四个问题:filp->private_data 在何时从 device 信息变成 proc;同 PID 重复打开为何 仍会新增 proc;从未 mmap 的 allocator 为什么可以释放;活动事务为何会推迟最终 kfree(proc)。 如果这四个问题都能从代码指出具体字段和分支,就已经建立了设备打开的完整生命周期模型。

继续阅读 事务数据生命周期,可以把这里创建的 proc/alloc owner 接到真实事务 buffer 的分配、交付和回收;回看 引用计数闭环,可以进一步理解 deferred release 为什么必须先清理 node、ref 和死亡通知,不能在 binder_release() 中直接释放整个 proc。