Skip to content

Binder节点管理

追踪 binder_node 的本地对象映射、节点创建、引用通知、异步队列和死亡清理。

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

Binder节点管理 ​

binder_node 是驱动对本地 Binder 对象的内核表示。它不是客户端看到的 handle,也不是跨进程复制的 C++ 指针:发送端用户空间的 flat_binder_object.binder/cookie 先在 owner proc 中定位或创建 node,驱动再为目标 proc 建立 ref,并把目标侧可见形式改写成 handle。

本文面向已经读过 Binder对象编码、引用计数闭环 和 Binder事务发送 的读者,重点解释 node 的 owner、索引、flags 固化、引用通知和 proc 死亡后的迁移。

本文不重复展开 ref 红黑树的完整分配算法,也不重新讲所有 strong/weak 边沿。这里的引用只在 node 生命周期真正受影响的位置出现:node 创建、BR_INCREFS/BR_ACQUIRE 通知、DONE 回执、异步队列和 dead-node 清理。

1. 三种对象 ​

表示所属可见范围关键身份
本地 Binder 对象发送端用户态owner procbinder 指针、cookie
binder_node发送端内核驱动全局逻辑ptr、cookie、node id
binder_ref/handle目标进程内核/用户态target procdescriptor、node 指针

node 的 ptr 和 cookie 只用于 owner proc 的本地对象校验;传到另一个 proc 后,驱动通常把 flat_binder_object 改成 BINDER_TYPE_HANDLE、binder=0、目标侧 descriptor。不要把 node 指针写成跨进程地址。

2. 节点结构 ​

源码文件:kernel/common/drivers/android/binder_internal.h

相关结构:struct binder_node

c
struct binder_node {
    int debug_id;
    spinlock_t lock;
    struct binder_work work;
    union {
        struct rb_node rb_node;
        struct hlist_node dead_node;
    };
    struct binder_proc *proc;
    struct hlist_head refs;
    int internal_strong_refs;
    int local_weak_refs;
    int local_strong_refs;
    int tmp_refs;
    binder_uintptr_t ptr;
    binder_uintptr_t cookie;
    u8 has_strong_ref:1;
    u8 pending_strong_ref:1;
    u8 has_weak_ref:1;
    u8 pending_weak_ref:1;
    u8 sched_policy:2;
    u8 inherit_rt:1;
    u8 accept_fds:1;
    u8 txn_security_ctx:1;
    u8 min_priority;
    bool has_async_transaction;
    struct list_head async_todo;
};

这里有三类状态:owner 与索引(proc/rb_node/ptr/cookie)、用户态引用握手(local/internal/pending/has)、事务调度(priority、async queue)。node 存活时使用 proc nodes 红黑树;owner proc 消失后,node 可能改用 dead_node 挂到全局 dead-node 表。

3. 创建查找 ​

3.1 用户态对象 ​

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

相关函数:Parcel::flattenBinder()

cpp
if (binder != nullptr) {
    BBinder* local = binder->localBinder();
    if (local) {
        obj.hdr.type = BINDER_TYPE_BINDER;
        obj.binder =
            reinterpret_cast<uintptr_t>(
                local->getWeakRefs());
        obj.cookie =
            reinterpret_cast<uintptr_t>(local);
        obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
        if (local->isRequestingSid())
            obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
        if (local->isInheritRt())
            obj.flags |= FLAT_BINDER_FLAG_INHERIT_RT;
    }
}

本地 BBinder 被写成 BINDER_TYPE_BINDER,binder 字段是弱引用地址,cookie 是本地对象地址;flags 把 fd、SID 和实时优先级继承能力带入驱动。Parcel::flattenBinder() 还会标记本地 Binder 已被 parceled,后续属性修改受到 BBinder 自身规则约束。

3.2 owner查找 ​

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

相关函数:binder_get_node()、binder_get_node_ilocked()

c
static struct binder_node *binder_get_node_ilocked(
        struct binder_proc *proc,
        binder_uintptr_t ptr) {
    struct rb_node *n = proc->nodes.rb_node;
    struct binder_node *node;

    while (n) {
        node = rb_entry(n, struct binder_node, rb_node);
        if (ptr < node->ptr)
            n = n->rb_left;
        else if (ptr > node->ptr)
            n = n->rb_right;
        else {
            binder_inc_node_tmpref_ilocked(node);
            return node;
        }
    }
    return NULL;
}

node 以 owner proc 的用户指针 ptr 为红黑树键。查找成功还会增加 tmp_ref;调用方必须在使用结束后 binder_put_node(),否则 node 可能被错误地认为仍在使用。

3.3 首次创建 ​

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

相关函数:binder_new_node()、binder_init_node_ilocked()

c
static struct binder_node *binder_new_node(
        struct binder_proc *proc,
        struct flat_binder_object *fp) {
    struct binder_node *node;
    struct binder_node *new_node =
        kzalloc(sizeof(*node), GFP_KERNEL);
    if (!new_node)
        return NULL;

    binder_inner_proc_lock(proc);
    node = binder_init_node_ilocked(
            proc, new_node, fp);
    binder_inner_proc_unlock(proc);
    if (node != new_node)
        kfree(new_node);
    return node;
}

创建同样采用锁外预分配、锁内查找/插入和竞态丢弃模式。已有相同 ptr 的 node 被复用并增加 tmp_ref;只有真正插入 nodes 红黑树的对象成为新的 node。

3.4 flags固化 ​

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

相关函数:binder_init_node_ilocked()

c
node->debug_id =
    atomic_inc_return(&binder_last_id);
node->proc = proc;
node->ptr = ptr;
node->cookie = cookie;
node->work.type = BINDER_WORK_NODE;
priority = flags &
    FLAT_BINDER_FLAG_PRIORITY_MASK;
node->sched_policy =
    (flags & FLAT_BINDER_FLAG_SCHED_POLICY_MASK) >>
    FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT;
node->min_priority =
    to_kernel_prio(node->sched_policy, priority);
node->accept_fds =
    !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
node->inherit_rt =
    !!(flags & FLAT_BINDER_FLAG_INHERIT_RT);
node->txn_security_ctx =
    !!(flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX);
spin_lock_init(&node->lock);
INIT_LIST_HEAD(&node->work.entry);
INIT_LIST_HEAD(&node->async_todo);

这些 flags 在 node 创建时解析并保存为 invariant;后续事务使用 node 的 priority、accept_fds 和 txn_security_ctx,不会每次从一个远程 handle 重新解释本地指针。

4. 传递转换 ​

4.1 Binder到ref ​

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

相关函数:binder_translate_binder()

c
node = binder_get_node(proc, fp->binder);
if (!node) {
    node = binder_new_node(proc, fp);
    if (!node)
        return -ENOMEM;
}
if (fp->cookie != node->cookie) {
    ret = -EINVAL;
    goto done;
}
if (security_binder_transfer_binder(
        proc->cred, target_proc->cred)) {
    ret = -EPERM;
    goto done;
}
ret = binder_inc_ref_for_node(
        target_proc, node,
        fp->hdr.type == BINDER_TYPE_BINDER,
        &thread->todo, &rdata);
if (ret)
    goto done;
fp->hdr.type =
    fp->hdr.type == BINDER_TYPE_BINDER
        ? BINDER_TYPE_HANDLE
        : BINDER_TYPE_WEAK_HANDLE;
fp->binder = 0;
fp->handle = rdata.desc;
fp->cookie = 0;
done:
    binder_put_node(node);

本地 node 通过 target proc 建立 ref;cookie 不匹配表示发送方伪造了本地对象关系,直接失败。转换后目标 buffer 中只保留 handle/descriptor,不把 owner 的 ptr/cookie 暴露给接收进程。

4.2 Handle到node ​

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

相关函数:binder_translate_handle()

c
node = binder_get_node_from_ref(
        proc, fp->handle,
        fp->hdr.type == BINDER_TYPE_HANDLE,
        &src_rdata);
if (!node)
    return -EINVAL;
binder_node_lock(node);
if (node->proc == target_proc) {
    fp->hdr.type =
        fp->hdr.type == BINDER_TYPE_HANDLE
            ? BINDER_TYPE_BINDER
            : BINDER_TYPE_WEAK_BINDER;
    fp->binder = node->ptr;
    fp->cookie = node->cookie;
    binder_inner_proc_lock(node->proc);
    binder_inc_node_nilocked(
        node,
        fp->hdr.type == BINDER_TYPE_BINDER,
        0, NULL);
    binder_inner_proc_unlock(node->proc);
    binder_node_unlock(node);
} else {
    binder_node_unlock(node);
    ret = binder_inc_ref_for_node(
        target_proc, node,
        fp->hdr.type == BINDER_TYPE_HANDLE,
        NULL, &dest_rdata);
    fp->binder = 0;
    fp->handle = dest_rdata.desc;
    fp->cookie = 0;
}
binder_put_node(node);

handle 传递有两种结果:若 node 本来就属于目标 proc,驱动可还原成目标本地 Binder 对象;否则在目标 proc 再建立一个 ref。这个分支解释了为什么同一个 Binder object 在不同跳转路径上可能表现为 node 或 handle。

5. 引用通知 ​

5.1 引用边沿 ​

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

相关函数:binder_inc_ref_olocked()、binder_dec_ref_olocked()

c
if (strong) {
    if (ref->data.strong == 0)
        ret = binder_inc_node(
            ref->node, 1, 1, target_list);
    ref->data.strong++;
} else {
    if (ref->data.weak == 0)
        ret = binder_inc_node(
            ref->node, 0, 1, target_list);
    ref->data.weak++;
}

只有 ref strong/weak 从 0 到 1 时,node internal 引用才增加;ref 的后续计数变化不会重复触发 node 边沿。node 侧 local 引用则来自用户态收到 BR 后发送 DONE 的确认,不等于 internal 引用。

5.2 Node work ​

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

相关函数:binder_thread_read() 的 BINDER_WORK_NODE 分支

c
strong = node->internal_strong_refs ||
          node->local_strong_refs;
weak = !hlist_empty(&node->refs) ||
       node->local_weak_refs ||
       node->tmp_refs || strong;
has_strong_ref = node->has_strong_ref;
has_weak_ref = node->has_weak_ref;

if (weak && !has_weak_ref) {
    node->has_weak_ref = 1;
    node->pending_weak_ref = 1;
    node->local_weak_refs++;
}
if (strong && !has_strong_ref) {
    node->has_strong_ref = 1;
    node->pending_strong_ref = 1;
    node->local_strong_refs++;
}

node work 被目标 proc 的某个线程读出后,驱动根据 internal/local/ref/tmp 状态决定是否向本地用户态发 BR_INCREFS、BR_ACQUIRE、BR_RELEASE 或 BR_DECREFS。work 不是每次 ref 变化都新建,而是用 node->work.entry 防止重复排队。

5.3 DONE闭环 ​

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

相关命令:BC_INCREFS_DONE、BC_ACQUIRE_DONE

c
node = binder_get_node(proc, node_ptr);
if (!node)
    break;
if (cookie != node->cookie) {
    binder_put_node(node);
    break;
}
binder_node_inner_lock(node);
if (cmd == BC_ACQUIRE_DONE)
    node->pending_strong_ref = 0;
else
    node->pending_weak_ref = 0;
binder_dec_node_nilocked(
    node, cmd == BC_ACQUIRE_DONE, 0);
binder_node_inner_unlock(node);
binder_put_node(node);

用户态 DONE 必须带回原 node ptr/cookie。cookie 错误、没有 pending 请求或 node 已不存在时,驱动不会把确认当成有效边沿;正确确认才会减少 pending/local 状态并可能触发 node 删除。

6. 异步队列 ​

6.1 Node队列 ​

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

相关函数:binder_proc_transaction()

c
if (oneway) {
    if (node->has_async_transaction)
        pending_async = true;
    else
        node->has_async_transaction = true;
}
if (!pending_async)
    binder_enqueue_work_ilocked(
        &t->work, &proc->todo);
else
    binder_enqueue_work_ilocked(
        &t->work, &node->async_todo);

第一笔 oneway 可以进入目标 proc work;已有 async transaction 时,后续 work 进入 node async_todo。node 负责同一实体的异步顺序,不是 thread todo 的替代名称。

6.2 释放转移 ​

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

相关函数:binder_free_buf()

c
if (buffer->async_transaction &&
        buffer->target_node) {
    struct binder_work *w;
    binder_node_inner_lock(buffer->target_node);
    w = binder_dequeue_work_head_ilocked(
            &buffer->target_node->async_todo);
    if (!w)
        buffer->target_node->has_async_transaction = false;
    else
        binder_enqueue_work_ilocked(
            w, &proc->todo);
    if (w)
        binder_wakeup_proc_ilocked(proc);
    binder_node_inner_unlock(buffer->target_node);
}

当前异步 buffer 释放后,node async_todo 的下一项才转入 proc todo;若队列空了,清除 has_async_transaction。队列顺序和 node 锁共同约束 oneway 事务的串行化边界。

7. Owner死亡 ​

7.1 Node释放 ​

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

相关函数:binder_node_release()

c
static int binder_node_release(
        struct binder_node *node, int refs) {
    struct binder_proc *proc = node->proc;
    binder_release_work(
        proc, &node->async_todo);
    binder_node_lock(node);
    binder_inner_proc_lock(proc);
    binder_dequeue_work_ilocked(&node->work);
    BUG_ON(!node->tmp_refs);
    if (hlist_empty(&node->refs) &&
            node->tmp_refs == 1) {
        binder_inner_proc_unlock(proc);
        binder_node_unlock(node);
        binder_free_node(node);
        return refs;
    }

如果 owner 死亡时没有远端 refs 且只剩当前临时引用,node 立即释放;否则必须把 node 与 proc 脱钩,不能继续挂在 owner 的 nodes 树。

7.2 Dead迁移 ​

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

相关函数:binder_node_release()

c
node->proc = NULL;
node->local_strong_refs = 0;
node->local_weak_refs = 0;
binder_inner_proc_unlock(proc);

spin_lock(&binder_dead_nodes_lock);
hlist_add_head(&node->dead_node,
        &binder_dead_nodes);
spin_unlock(&binder_dead_nodes_lock);

迁移到 dead_nodes 后 node 仍可能因远端 refs、tmp_refs 或死亡通知存活,但已经没有 owner proc。后续 node tmp ref 的保护锁从 proc inner lock 转为 dead_nodes lock;读取 node debug 信息也必须按 dead-node 规则处理。

7.3 死亡通知 ​

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

相关函数:binder_node_release()

c
hlist_for_each_entry(ref,
        &node->refs, node_entry) {
    if (!ref->death)
        continue;
    ref->death->work.type =
            BINDER_WORK_DEAD_BINDER;
    binder_enqueue_work_ilocked(
            &ref->death->work,
            &ref->proc->todo);
    binder_wakeup_proc_ilocked(
            ref->proc);
}

每个拥有 death notification 的远端 ref 都收到 proc todo work;通知消费者是远端线程池,而不是死亡 owner 的 thread。具体 BR_DEAD_BINDER 与 DONE 协议属于后续死亡通知文章。

8. 节点删除 ​

8.1 无引用条件 ​

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

相关函数:binder_dec_node_nilocked()

c
if (hlist_empty(&node->refs) &&
    !node->local_strong_refs &&
    !node->local_weak_refs &&
    !node->tmp_refs) {
    if (proc) {
        binder_dequeue_work_ilocked(&node->work);
        rb_erase(&node->rb_node,
                 &proc->nodes);
    } else {
        hlist_del(&node->dead_node);
    }
    return true;
}

node 删除需要同时满足:没有远端 refs、没有 local strong/weak、没有 tmp_refs。owner 活着时从 proc nodes 树移除;owner 已死时从 dead_nodes 链表移除。返回 true 的调用者才可以执行 binder_free_node()。

8.2 最终free ​

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

相关函数:binder_free_node()

c
static void binder_free_node(
        struct binder_node *node) {
    kfree(node);
    binder_stats_deleted(
            BINDER_STAT_NODE);
}

kfree 很短,但它依赖前面所有结构关系已经解除。不能看到这个函数就推断“最后一个本地 strong 引用释放时立即 free”;远端 ref、node work、tmp ref 和 owner 死亡迁移都会延迟这一刻。

9. 测试输入 ​

9.1 Parcel标记 ​

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

相关测试:WasParceled

cpp
TEST_F(BinderLibTest, WasParceled) {
    auto binder = sp<BBinder>::make();
    EXPECT_FALSE(binder->wasParceled());
    Parcel data;
    data.writeStrongBinder(binder);
    EXPECT_TRUE(binder->wasParceled());
}

输入是一个本地 BBinder 和一次 writeStrongBinder。断言只证明用户态 flatten 会标记对象已被 parceled;它不证明 node 已经进入驱动,因为 Parcel 可能尚未 flush 到 ioctl。

9.2 Oneway顺序 ​

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

相关测试:OnewayQueueing

cpp
EXPECT_THAT(pollServer->transact(
    BINDER_LIB_TEST_DELAYED_CALL_BACK,
    data, nullptr, TF_ONE_WAY),
    StatusEq(NO_ERROR));
EXPECT_THAT(pollServer->transact(
    BINDER_LIB_TEST_DELAYED_CALL_BACK,
    data2, nullptr, TF_ONE_WAY),
    StatusEq(NO_ERROR));
EXPECT_THAT(callBack->waitEvent(2),
    StatusEq(NO_ERROR));
EXPECT_THAT(callBack2->waitEvent(2),
    StatusEq(NO_ERROR));

第一笔 callback 延迟,第二笔立即返回;测试依赖单线程 poll server 让第二笔进入 async_todo,并检查回调顺序。它证明 node 异步队列的可观察顺序,不证明多 node、多线程下所有调度都串行。

9.3 死亡通知 ​

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

相关测试:DeathNotificationStrongRef、DeathNotificationThread

测试让服务以 oneway 事务退出,再等待 death recipient 回调;DeathNotificationThread 还验证已阻塞注册线程时,通知会投递到 proc workqueue 的其他合格线程。它覆盖 node owner 死亡后的远端可见结果,不替代驱动 node release 的所有竞态证明。

10. 失败边界 ​

阶段失败或竞态结果
本地对象查找cookie 不匹配-EINVAL,不建立目标 ref
node 创建kzalloc 失败事务失败,目标对象不变
transfer 权限SELinux hook 拒绝-EPERM/failed reply
ref 建立descriptor/ref 分配失败释放临时 node/ref 状态
DONE确认node 不存在、cookie/pending 错误忽略或记录用户错误
owner退出仍有远端 refnode 迁移 dead_nodes,发送死亡 work
最终删除tmp/ref/local计数非零延迟 free_node

11. 源码复现 ​

本文主线:

text
Parcel::flattenBinder
→ flat_binder_object
→ binder_get_node/binder_new_node
→ owner proc nodes红黑树
→ binder_translate_binder
→ target proc binder_ref
→ node work与BR引用通知
→ oneway async_todo
→ owner死亡时dead_nodes
→ refs/local/tmp归零
→ binder_free_node

源码搜索:

bash
rg -n "flattenBinder|setParceled|FLAT_BINDER_FLAG" \
  frameworks/native/libs/binder/Parcel.cpp \
  frameworks/native/libs/binder/Binder.cpp

rg -n "binder_new_node|binder_init_node_ilocked|binder_get_node_ilocked|binder_free_node" \
  kernel/common/drivers/android/binder.c

rg -n "binder_translate_binder|binder_translate_handle|binder_inc_ref_for_node" \
  kernel/common/drivers/android/binder.c

rg -n "binder_node_release|binder_dec_node_nilocked|binder_dead_nodes" \
  kernel/common/drivers/android/binder.c

rg -n "WasParceled|OnewayQueueing|DeathNotificationStrongRef|DeathNotificationThread" \
  frameworks/native/libs/binder/tests/binderLibTest.cpp

如果能够解释为什么 cookie 是 owner 校验而不是远端地址、为什么目标进程看到的是 ref/handle、为什么 node work 需要用户态 DONE、以及 owner 死亡后 node 为什么仍可存活,就已经掌握了 binder_node 的本地节点生命周期。

DeadOwner 不会立即等于 Reclaimed:远端 ref、死亡通知和异步 work 都可能暂时持有 node。