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 proc | binder 指针、cookie |
binder_node | 发送端内核 | 驱动全局逻辑 | ptr、cookie、node id |
binder_ref/handle | 目标进程内核/用户态 | target proc | descriptor、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
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()
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()
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()
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()
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()
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()
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()
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 分支
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
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()
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()
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()
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()
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()
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()
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()
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
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
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退出 | 仍有远端 ref | node 迁移 dead_nodes,发送死亡 work |
| 最终删除 | tmp/ref/local计数非零 | 延迟 free_node |
11. 源码复现
本文主线:
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源码搜索:
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。
