Skip to content

引用计数闭环

追踪 BpBinder、binder_ref、binder_node 的强弱引用、通知握手、延迟释放与进程退出清理。

基于android-17.0.0_r1
AndroidBinder引用计数生命周期源码阅读

引用计数闭环 ​

Binder 的引用计数不是把 native sp 智能指针直接跨进程复制。至少有四个不同层次的状态: 客户端 BpBinder 的 C++ strong/weak 生命周期、客户端进程的 binder_ref strong/weak、服务端 binder_node 的 internal/local 计数,以及驱动通知服务端后等待 DONE 的 pending 状态。

本文面向已经读过 Binder代理生命周期 和 Binder对象编码 的读者,专门回答引用何时从 0 变 1,哪个边沿触发 BR_ACQUIRE/BR_INCREFS,为什么 release 可能延迟,以及 node/ref/proc 何时真正清理。

1. 三层状态 ​

计数归属 ​

层结构状态owner
native客户端BpBinderRefBase strong/weak、handlelibbinder
客户端内核binder_refdata.strong、data.weak、descclient proc
服务端内核binder_nodeinternal/local strong/weak、tmp、pendingnode
服务端用户态BBinderRefBase strong/weak、DONE确认server process

同一 proc 对同一 node 只有一个 ref,但 ref 内的 strong/weak 可以多次变化。node 的 internal 计数来自远端 ref,local 计数来自本地用户态收到 BR 通知后的确认,两者不能合成一个数字。

2. 客户端引用 ​

weak入口 ​

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

相关函数:BpBinder::BpBinder()

cpp
BpBinder::BpBinder(BinderHandle&& handle,
        int32_t trackedUid)
    : BpBinder(Handle(handle)) {
    mTrackedUid = trackedUid;
    IPCThreadState::self()->incWeakHandle(
            binderHandle(), this);
}

创建 BpBinder 时先为 handle 提交 BC_INCREFS,这是代理存在的弱引用基础,不等于已经有业务 strong 引用。

strong入口 ​

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

相关函数:onFirstRef()、onLastStrongRef()

cpp
void BpBinder::onFirstRef() {
    IPCThreadState* ipc =
            IPCThreadState::self();
    if (ipc)
        ipc->incStrongHandle(
                binderHandle(), this);
}

void BpBinder::onLastStrongRef(
        const void* /*id*/) {
    IPCThreadState* ipc =
            IPCThreadState::self();
    if (ipc && !kDecStrongLast)
        ipc->decStrongHandle(
                binderHandle());
    /* 清理 obituary 等本地状态 */
    if (ipc && kDecStrongLast)
        ipc->decStrongHandle(
                binderHandle());
}

具体 BC_RELEASE 位置受 kDecStrongLast 构建分支影响。稳定结论是 strong 从无到有和从有到无 对应 ACQUIRE/RELEASE,而不是固定某一行在所有构建中生效。

命令缓冲 ​

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

cpp
void IPCThreadState::incStrongHandle(
        int32_t handle, BpBinder* proxy) {
    mOut.writeInt32(BC_ACQUIRE);
    mOut.writeInt32(handle);
    if (!flushIfNeeded()) {
        proxy->incStrong(mProcess.get());
        mPostWriteStrongDerefs.push(proxy);
    }
}

void IPCThreadState::incWeakHandle(
        int32_t handle, BpBinder* proxy) {
    mOut.writeInt32(BC_INCREFS);
    mOut.writeInt32(handle);
    if (!flushIfNeeded()) {
        proxy->getWeakRefs()->incWeak(
                mProcess.get());
        mPostWriteWeakDerefs.push(
                proxy->getWeakRefs());
    }
}

命令尚未被驱动消费时,libbinder 暂时增加本地保护引用。write buffer 成功消费后, processPostWriteDerefs 才撤销这些临时引用,避免代理在命令提交途中析构。

3. ref边沿 ​

增加逻辑 ​

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

相关函数:binder_inc_ref_olocked()

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

只有 ref 的 strong/weak 从 0 变 1 时,才影响 node internal 状态。同一 ref 的第二个 strong 只增加 ref.data.strong,不再重复增加 node internal_strong_refs。

减少逻辑 ​

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

相关函数:binder_dec_ref_olocked()

c
if (strong) {
    if (ref->data.strong == 0)
        return false;
    ref->data.strong--;
    if (ref->data.strong == 0)
        binder_dec_node(
                ref->node, 1, 1);
} else {
    if (ref->data.weak == 0)
        return false;
    ref->data.weak--;
}

if (ref->data.strong == 0 &&
        ref->data.weak == 0) {
    binder_cleanup_ref_olocked(ref);
    return true;
}

strong 归零减少 node internal strong;strong 和 weak 同时归零才清理 ref。无效递减只记录协议 错误,不会让计数变成负数后继续清理。

BC入口 ​

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

c
bool strong = cmd == BC_ACQUIRE ||
        cmd == BC_RELEASE;
bool increment = cmd == BC_INCREFS ||
        cmd == BC_ACQUIRE;

ret = binder_update_ref_for_handle(
        proc, target,
        increment, strong,
        &rdata);

四条命令共享入口,strong 与 increment 两个布尔值决定操作哪个计数和方向。handle 0 的 context manager 还有单独分支。

4. node通知 ​

internal与local ​

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

相关函数:binder_inc_node_nilocked()

c
if (strong) {
    if (internal)
        node->internal_strong_refs++;
    else
        node->local_strong_refs++;

    if (!node->has_strong_ref &&
            target_list) {
        binder_enqueue_deferred_thread_work_ilocked(
                thread, &node->work);
    }
} else {
    if (!internal)
        node->local_weak_refs++;

    if (!node->has_weak_ref &&
            target_list &&
            list_empty(&node->work.entry)) {
        binder_enqueue_work_ilocked(
                &node->work, target_list);
    }
}

远端 ref 贡献 internal strong;服务进程用户态确认后形成 local strong/weak。node work 让驱动 稍后向服务线程输出 BR_ACQUIRE/BR_INCREFS。

BR到DONE ​

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

cpp
case BR_ACQUIRE:
    refs = (RefBase::weakref_type*)
            mIn.readPointer();
    obj = (BBinder*)mIn.readPointer();
    obj->incStrong(mProcess.get());
    mOut.writeInt32(BC_ACQUIRE_DONE);
    mOut.writePointer((uintptr_t)refs);
    mOut.writePointer((uintptr_t)obj);
    break;

case BR_INCREFS:
    refs = (RefBase::weakref_type*)
            mIn.readPointer();
    obj = (BBinder*)mIn.readPointer();
    refs->incWeak(mProcess.get());
    mOut.writeInt32(BC_INCREFS_DONE);
    mOut.writePointer((uintptr_t)refs);
    mOut.writePointer((uintptr_t)obj);
    break;

服务端先改变 RefBase,再回写 DONE。驱动校验 node_ptr、cookie 和 pending 标志;DONE 不是 可重复调用的幂等命令。

延迟释放 ​

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

cpp
case BR_RELEASE:
    refs = (RefBase::weakref_type*)
            mIn.readPointer();
    obj = (BBinder*)mIn.readPointer();
    mPendingStrongDerefs.push(obj);
    break;

case BR_DECREFS:
    refs = (RefBase::weakref_type*)
            mIn.readPointer();
    mIn.readPointer();
    mPendingWeakDerefs.push(refs);
    break;

BR_RELEASE/BR_DECREFS 先进入 pending 容器;输入命令队列安全清空后,processPendingDerefs 才执行 decStrong/decWeak。析构可能发起新 Binder 命令,因此不能在解析中任意重入。

5. node删除 ​

删除条件 ​

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

相关函数:binder_dec_node_nilocked()

c
if (strong) {
    if (internal)
        node->internal_strong_refs--;
    else
        node->local_strong_refs--;

    if (node->local_strong_refs ||
            node->internal_strong_refs)
        return false;
} else {
    if (!internal)
        node->local_weak_refs--;

    if (node->local_weak_refs ||
            node->tmp_refs ||
            !hlist_empty(&node->refs))
        return false;
}

if (proc &&
        (node->has_strong_ref ||
         node->has_weak_ref)) {
    if (list_empty(&node->work.entry))
        binder_enqueue_work_ilocked(
                &node->work, &proc->todo);
} else if (hlist_empty(&node->refs) &&
        !node->local_strong_refs &&
        !node->local_weak_refs &&
        !node->tmp_refs) {
    return true;
}
return false;

所有 strong 归零只表示可以通知服务端释放 strong;真正删除还要求 refs、local weak、tmp ref 和 pending work 状态都收束。

ref删除 ​

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

相关函数:binder_cleanup_ref_olocked()

c
rb_erase(&ref->rb_node_desc,
        &ref->proc->refs_by_desc);
rb_erase(&ref->rb_node_node,
        &ref->proc->refs_by_node);

binder_node_inner_lock(ref->node);
if (ref->data.strong)
    binder_dec_node_nilocked(
            ref->node, 1, 1);
hlist_del(&ref->node_entry);
delete_node = binder_dec_node_nilocked(
        ref->node, 0, 1);
binder_node_inner_unlock(ref->node);

ref 清理同时从 handle/node 两棵索引树和 node.refs 链表移除。delete_node 只在 node 的其他条件 也满足时成立。

6. 进程退出 ​

node死亡 ​

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

相关函数:binder_node_release()

c
node->proc = NULL;
node->local_strong_refs = 0;
node->local_weak_refs = 0;
hlist_add_head(
        &node->dead_node,
        &binder_dead_nodes);

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);
}

服务进程退出后 node 进入 dead list;仍持有 ref 的客户端可收到死亡通知。node 本地计数清零, 但 node 可能因 refs/tmp_refs 暂时保留。

proc清理 ​

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

相关函数:binder_deferred_release()

c
proc->is_dead = true;

while ((nodeEntry =
        rb_first(&proc->nodes))) {
    node = rb_entry(
            nodeEntry,
            struct binder_node,
            rb_node);
    binder_node_release(
            node, incoming_refs);
}

while ((refEntry =
        rb_first(&proc->refs_by_desc))) {
    ref = rb_entry(
            refEntry,
            struct binder_ref,
            rb_node_desc);
    binder_cleanup_ref_olocked(ref);
    binder_free_ref(ref);
}

进程退出同时释放本进程发布的 nodes 和持有的 outgoing refs。跨进程引用生命周期必须处理两种 方向,而不是只看客户端 proxy 析构。

7. 测试输入 ​

引用命令 ​

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

相关测试:IncRefsAcquireReleaseDecRefs()

cpp
const uint32_t commands[] = {
    BC_INCREFS, 0,
    BC_ACQUIRE, 0,
    BC_RELEASE, 0,
    BC_DECREFS, 0,
};

binder_write_read bwr = {};
bwr.write_buffer =
        reinterpret_cast<uintptr_t>(
                commands);
bwr.write_size = sizeof(commands);

binderTestIoctl(
        BINDER_WRITE_READ, &bwr);
EXPECT_EQ(
        sizeof(commands),
        bwr.write_consumed);

测试验证四条引用命令可以在一次 write buffer 中完整消费。它不能证明 node 已经发生业务调用, 也不能证明所有通知均已回写 DONE。

本地weak升级 ​

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

相关测试:PromoteLocal()

测试创建 BBinder strong 与 weak,strong 存在时 promote 成功;清空全部 strong 后再次 promote 返回 null。它验证本地 RefBase 语义,是理解 BpBinder onFirst/onLast 的前置,不等于跨进程 ref 测试。

死亡与失效 ​

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

相关测试:DeathNotificationStrongRef()、DeathNotificationMultiple()、FreedBinder()、WeakRejected()

测试分别覆盖 strong proxy 持有时服务死亡、多客户端 death recipient、失效 handle 重新使用以及 恶意 weak object 重复发送。断言包括 death callback、DEAD_OBJECT/FAILED_TRANSACTION、BAD_VALUE 以及服务仍能 ping。它们能验证失败清理不会破坏其他对象,不能证明所有厂商内核竞态。

8. 失败边界 ​

无效递减 ​

ref strong/weak 已为 0 时继续 BC_RELEASE/BC_DECREFS 会记录 user error,不会继续把 node 计数 减成负数。

DONE错配 ​

node 不存在、cookie 不匹配或没有 pending acquire/increfs 时,DONE 会被拒绝或记录协议错误。 DONE 不是普通状态确认 API。

延迟析构 ​

post-write deref、pending deref 和 driver 命令批处理都会延迟析构。观察 C++ destructor 时间, 不能直接推导最后一条 BC_RELEASE 的时间。

旧代理 ​

node 死亡后 BpBinder 仍可能被本地 sp/wp 持有,但 transact 已返回 DEAD_OBJECT。对象内存存在 和远程目标存活是两个状态。

9. 阅读边界 ​

强弱引用闭环 ​

驱动引用、native BpBinder 和本地 node 的计数不是同一个整数;每次握手都要说明命令消费者和清理 owner。 图只表达闭环,具体强弱计数仍以本文各源码段和测试断言为准。

本文证明了 BpBinder strong/weak 到 BC 命令、ref 0→1/1→0 边沿、node internal/local 计数、 BR/DONE 握手、延迟 deref、ref/node 删除和 proc 退出。没有展开完整 death notification 命令、 冻结回调、allocator、线程池、RPC Binder 或 SELinux。

继续阅读 Binder代理生命周期 复习 Java/native proxy owner。 排查引用问题时按“RefBase → BC → ref 边沿 → node work → BR/DONE → pending deref → cleanup” 顺序取证。