Binder的C/S模型
“Client 调用 Proxy,Server 执行 Stub”是 Binder 的入门口诀,但它隐藏了三个关键事实:
- Client 和 Server 是一次事务中的角色,不是进程的永久标签;
- 同一进程既能持有远程 BpBinder,也能发布本地 BBinder;
- 接口转换可能在本地直接短路,根本不会进入驱动。
本文面向已经读过 Binder调用源码地图 的读者,沿 native libbinder 和 kernel/common 真实源码回答一个问题:一个 IBinder 接口在本地、远程、嵌套回调三种情况下,谁拥有对象、谁选择线程、谁保存事务栈,状态如何闭合。
本文不展开完整 AIDL 代码生成、死亡通知、引用计数专题和线程池上限。读完后,读者应能区分 queryLocalInterface、BpInterface、BnInterface、binder_ref、binder_node、transaction_stack,并解释“服务方法反向回调客户端”为什么仍然遵守同一套 C/S 状态规则。
1. 角色边界
角色归属
一次普通调用可以表示为:
| 时刻 | 调用方角色 | 被调用方角色 | 关键 owner |
|---|---|---|---|
| 接口获取 | 取服务的进程 | ServiceManager | handle/ref |
| 事务发送 | 持有 BpBinder 的线程 | 持有 BBinder 的进程 | binder_transaction |
| 服务回调 | 原服务线程 | 原客户端的 BBinder | 新 transaction |
| reply | 等待 reply 的原线程 | 发送 BC_REPLY 的服务线程 | transaction_stack |
Server 不是“永远只接收请求”。当服务把一个 callback Binder 放进 Parcel 后,客户端也会成为 被调用方,服务进程暂时成为新的 Client。角色由当前 transaction 的 target 和 from 决定。
三个对象
| 对象 | 所在进程 | 作用 |
|---|---|---|
| BpBinder | 远程对象的持有方 | 用 handle 发起 native 事务 |
| BBinder | 本地对象的拥有方 | 用 ptr/cookie 接收事务 |
| binder_ref/node | 内核 | 在进程间翻译引用和目标 |
handle 只在持有它的进程中有意义;node 的 ptr/cookie 只在本地对象所属进程中使用。跨进程传递 Binder 对象时,驱动会把 BINDER_TYPE_BINDER 或 BINDER_TYPE_HANDLE 转换成目标进程可用的 引用形态。
2. 接口转换
asInterface
源码文件:frameworks/native/libs/binder/include/binder/IInterface.h
相关宏:DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0()
#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0(ITYPE, INAME, BPTYPE) \
const ::android::String16& ITYPE::getInterfaceDescriptor() const { \
return ITYPE::descriptor; \
} \
::android::sp<ITYPE> ITYPE::asInterface( \
const ::android::sp<::android::IBinder>& obj) { \
::android::sp<ITYPE> intr; \
if (obj != nullptr) { \
intr = ::android::sp<ITYPE>::cast( \
obj->queryLocalInterface(ITYPE::descriptor)); \
if (intr == nullptr) { \
intr = ::android::sp<BPTYPE>::make(obj); \
} \
} \
return intr; \
}asInterface 先调用 queryLocalInterface,再决定是否创建 BpInterface。这个顺序就是 C/S 模型的 第一个分叉:同一进程拿到 BnInterface 时可以返回本地接口;远程对象默认返回空,才构造代理。 所以“调用接口一定经过 Binder 驱动”是错误的。
本地接口
源码文件:frameworks/native/libs/binder/include/binder/IInterface.h
相关模板:BnInterface<INTERFACE>
template<typename INTERFACE>
inline sp<IInterface> BnInterface<INTERFACE>::queryLocalInterface(
const String16& _descriptor)
{
if (_descriptor == INTERFACE::descriptor)
return sp<IInterface>::fromExisting(this);
return nullptr;
}
template<typename INTERFACE>
IBinder* BnInterface<INTERFACE>::onAsBinder()
{
return this;
}本地匹配返回同一个对象的接口视图,调用方后续会直接调用 C++ 虚函数。这里没有 Parcel、handle 或 transaction_stack;如果调试时看到本地调用没有 binder transaction,这是设计行为而不是丢日志。
远程接口
源码文件:frameworks/native/libs/binder/include/binder/IInterface.h
template<typename INTERFACE>
inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
: BpRefBase(remote)
{
}
template<typename INTERFACE>
inline IBinder* BpInterface<INTERFACE>::onAsBinder()
{
return remote();
}远程接口只保存 remote IBinder;生成的 BpFoo 方法通常把参数写入 Parcel,再调用 remote()->transact。 BpInterface 不拥有服务对象,它只拥有一份远程引用的用户态表示。
3. 本地实体
BBinder接口
源码文件:frameworks/native/libs/binder/Binder.cpp
相关函数:IBinder::queryLocalInterface()、BBinder::localBinder()
sp<IInterface> IBinder::queryLocalInterface(
const String16& /*descriptor*/)
{
return nullptr;
}
BBinder* IBinder::localBinder()
{
return nullptr;
}
BBinder* BBinder::localBinder()
{
return this;
}IBinder 的默认 queryLocalInterface 返回空;BnInterface 才按 descriptor 提供本地接口。localBinder 返回 this 是服务端判断“这是本地实体”的依据,BpBinder 则实现 remoteBinder 返回自身。
事务入口
源码文件:frameworks/native/libs/binder/Binder.cpp
相关函数:BBinder::transact()
status_t BBinder::transact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
data.setDataPosition(0);
if (reply != nullptr && (flags & FLAG_CLEAR_BUF)) {
reply->markSensitive();
}
status_t err = NO_ERROR;
switch (code) {
case PING_TRANSACTION:
err = pingBinder();
break;
case EXTENSION_TRANSACTION:
LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr");
err = reply->writeStrongBinder(getExtension());
break;
case DEBUG_PID_TRANSACTION:
LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr");
err = reply->writeInt32(getDebugPid());
break;
default:
err = onTransact(code, data, reply, flags);
break;
}
if (reply != nullptr)
reply->setDataPosition(0);
return err;
}BBinder 先处理系统事务码,再把用户 code 交给 onTransact。reply 在本地调用路径也会被重置到 起始位置,因此本地和远程接口都可以用同一套 Parcel 读取约定,但它们的线程和内核路径不同。
默认存根
源码文件:frameworks/native/libs/binder/Binder.cpp
相关函数:BBinder::onTransact()
status_t BBinder::onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t /*flags*/)
{
switch (code) {
case INTERFACE_TRANSACTION:
LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr");
reply->writeString16(getInterfaceDescriptor());
return NO_ERROR;
default:
return UNKNOWN_TRANSACTION;
}
}实际 BnInterface 子类会覆写 onTransact,AIDL Stub 还会检查 descriptor、读取参数和调用服务实现。 默认 BBinder 不知道业务方法;未知 code 返回 UNKNOWN_TRANSACTION,这也是代理端区分接口不匹配的重要 失败入口。
4. 远程代理
BpBinder入口
源码文件:frameworks/native/libs/binder/BpBinder.cpp
相关函数:BpBinder::transact()
status_t BpBinder::transact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
if (mAlive) {
status_t status;
if (isRpcBinder()) {
status = rpcSession()->transact(
sp<IBinder>::fromExisting(this), code, data, reply, flags);
} else {
status = IPCThreadState::self()->transact(
binderHandle(), code, data, reply, flags);
}
if (status == DEAD_OBJECT)
mAlive = 0;
return status;
}
return DEAD_OBJECT;
}内核 Binder 分支把 handle 交给 IPCThreadState;收到 DEAD_OBJECT 后只把当前代理标记为死亡, 不会自动把服务重启后的新对象塞回旧代理。恢复必须由上层重新查找服务或重建接口。
handle意义
源码文件:frameworks/native/libs/binder/ProcessState.cpp
相关函数:getStrongProxyForHandle()
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
std::function<void()> postTask;
std::unique_lock<std::mutex> _l(mLock);
handle_entry* e = lookupHandleLocked(handle);
if (e != nullptr) {
IBinder* b = e->binder;
if (b == nullptr || !e->refs->attemptIncWeak(this)) {
sp<BpBinder> bp = BpBinder::PrivateAccessor::create(
handle, &postTask);
e->binder = bp.get();
if (bp)
e->refs = bp->getWeakRefs();
result = bp;
} else {
result.force_set(b);
e->refs->decWeak(this);
}
}
_l.unlock();
if (postTask)
postTask();
return result;
}实际源码还包含 handle 0 的 context manager 特例、引用计数和驱动命令;这里保留核心 owner: ProcessState 按本进程 handle 表缓存 BpBinder。两个线程拿同一 handle 时,可能共享同一个 native 代理对象,但每次 transact 仍由各自 IPCThreadState 和驱动线程状态执行。
5. 引用转换
Binder对象写入
源码文件:frameworks/native/libs/binder/Parcel.cpp
相关函数:Parcel::flattenBinder()
status_t Parcel::flattenBinder(const sp<IBinder>& binder)
{
flat_binder_object obj;
if (binder) {
if (BBinder* local = binder->localBinder()) {
obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);
} else {
BpBinder* proxy = binder->remoteBinder();
obj.hdr.type = BINDER_TYPE_HANDLE;
obj.handle = proxy->binderHandle();
obj.cookie = 0;
}
} else {
obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = 0;
obj.cookie = 0;
}
return writeObject(obj, false);
}写入 Parcel 的 Binder 对象有两种主要形态:本地对象携带 ptr/cookie,远程代理携带 handle。 这不是把 C++ 对象字节复制给对端,而是让驱动在目标进程建立或复用对应的引用。
驱动转换
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_translate_binder()、binder_translate_handle()
static int binder_translate_binder(struct flat_binder_object *fp,
struct binder_transaction *t, struct binder_thread *thread)
{
struct binder_proc *proc = thread->proc;
struct binder_proc *target_proc = t->to_proc;
struct binder_node *node;
struct binder_ref_data rdata;
node = binder_get_node(proc, fp->binder);
if (!node)
node = binder_new_node(proc, fp);
if (security_binder_transfer_binder(proc->cred, target_proc->cred))
return -EPERM;
binder_inc_ref_for_node(target_proc, node,
fp->hdr.type == BINDER_TYPE_BINDER,
&thread->todo, &rdata);
fp->hdr.type = BINDER_TYPE_HANDLE;
fp->binder = 0;
fp->handle = rdata.desc;
fp->cookie = 0;
return 0;
}发送一个本地 Binder 时,驱动在发送方 proc 找到或创建 node,再在目标 proc 增加 ref,并把对象 改写为 handle。反向发送一个 handle 时,binder_translate_handle 会查找源 ref;若目标进程正是 node 所属进程,可以恢复 BINDER_TYPE_BINDER 和 ptr/cookie,否则在目标进程创建新的 ref。
两个引用树
源码文件:kernel/common/drivers/android/binder_internal.h
struct binder_ref {
struct binder_ref_data data;
struct rb_node rb_node_desc;
struct rb_node rb_node_node;
struct hlist_node node_entry;
struct binder_proc *proc;
struct binder_node *node;
};
struct binder_proc {
struct rb_root refs_by_desc;
struct rb_root refs_by_node;
struct rb_root nodes;
struct rb_root threads;
struct list_head todo;
struct binder_alloc alloc;
};refs_by_desc 支持从 handle 查找,refs_by_node 支持从 node 查找;node->refs 还记录哪些进程持有该 节点的引用。这个多重索引解释了为什么 handle 不是全局对象编号,也解释了为什么清理一个进程 时需要同时处理 node、ref 和 todo。
6. 事务栈
目标选择
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()
if (reply) {
in_reply_to = thread->transaction_stack;
if (in_reply_to == NULL) {
return_error = BR_FAILED_REPLY;
goto err_empty_call_stack;
}
thread->transaction_stack = in_reply_to->to_parent;
target_thread = binder_get_txn_from_and_acq_inner(in_reply_to);
} else {
ref = binder_get_ref_olocked(proc, tr->target.handle, true);
if (ref) {
target_node = binder_get_node_refs_for_txn(
ref->node, &target_proc, &return_error);
} else {
return_error = BR_FAILED_REPLY;
}
}新事务按 target handle 找 node 和目标进程;reply 不重新按服务名查找,而是从当前服务线程的 transaction_stack 找回原调用线程。回复栈损坏会生成 BR_FAILED_REPLY 或 BR_DEAD_REPLY。
同步事务
源码文件:kernel/common/drivers/android/binder.c
if (!(t->flags & TF_ONE_WAY)) {
binder_inner_proc_lock(proc);
binder_enqueue_deferred_thread_work_ilocked(thread, tcomplete);
t->from_parent = thread->transaction_stack;
thread->transaction_stack = t;
binder_inner_proc_unlock(proc);
return_error = binder_proc_transaction(
t, target_proc, target_thread);
}同步事务发送前把当前事务压入 from 线程的栈;目标服务线程收到事务后,又把事务压入自己的 transaction_stack。服务发送 BC_REPLY 时,驱动利用这些 parent/from 字段把 reply 放回原等待 线程。oneway 没有同样的业务 reply 栈,因此不能用同步调用的完成条件解释它。
嵌套调用
服务处理客户端请求时,如果服务又同步调用第三个进程,Binder 会尝试依据 transaction_stack 选择关联线程,并记录 from_parent。嵌套调用不是“新开一个固定线程”,而是当前 Binder 线程和 驱动事务栈的状态组合;死锁风险来自锁、线程等待和回调方向,而不是 C/S 名称本身。
7. 回调反转
传入回调
源码文件:frameworks/native/libs/binder/tests/binderLibTest.cpp
相关测试:BinderLibTest.CallBack()
TEST_F(BinderLibTest, CallBack)
{
Parcel data, reply;
sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack();
data.writeStrongBinder(callBack);
EXPECT_THAT(m_server->transact(
BINDER_LIB_TEST_NOP_CALL_BACK, data, &reply, TF_ONE_WAY),
StatusEq(NO_ERROR));
EXPECT_THAT(callBack->waitEvent(5), StatusEq(NO_ERROR));
EXPECT_THAT(callBack->getResult(), StatusEq(NO_ERROR));
}客户端把本地 callback Binder 写入 Parcel,服务收到后取得一个指向客户端 node/ref 的远程接口, 再以 oneway transaction 回调客户端。原来的 Client 在这一笔新事务中变成 Server;原服务线程 变成 Client。
回调时序
测试的关键断言不是“回调函数被调用”,而是 waitEvent 和 getResult 都成功;这说明服务端 确实完成了回调事务,但不能推出回调线程、调度延迟或任何服务的通用顺序。
嵌套验证
源码文件:frameworks/native/libs/binder/tests/binderLibTest.cpp
相关测试:BinderLibTest.IndirectGetId2()、BinderLibTest.IndirectGetId3()
TEST_F(BinderLibTest, IndirectGetId3)
{
Parcel data, reply;
int32_t serverId[3];
data.writeInt32(countof(serverId));
for (size_t i = 0; i < countof(serverId); i++) {
sp<IBinder> server = addServer(&serverId[i]);
data.writeStrongBinder(server);
data.writeInt32(BINDER_LIB_TEST_INDIRECT_TRANSACTION);
BinderLibTestBundle nested;
nested.writeInt32(1);
nested.writeStrongBinder(m_server);
nested.writeInt32(BINDER_LIB_TEST_GET_ID_TRANSACTION);
nested.appendTo(&data);
}
ASSERT_THAT(m_server->transact(
BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply),
StatusEq(NO_ERROR));
}输入包含三个动态 server Binder,并在每个 bundle 内再次放入 m_server 和下一层事务码;动作是 让服务端继续调用这些 Binder;断言随后逐层读取 id、count 和 bundle 边界。它能证明多层对象 传递和嵌套事务可以闭合,不能证明任意业务锁组合都不会死锁。
8. 失败边界
本地不匹配
queryLocalInterface descriptor 不匹配时返回 null,asInterface 会构造远程代理;如果调用方 误把不同接口当成同一接口,后续可能得到 UNKNOWN_TRANSACTION 或 descriptor 校验失败。
无效引用
驱动在 binder_transaction 中找不到 handle 对应的 ref,或者 node 已死亡,会返回失败命令; 客户端可能看到 BR_FAILED_REPLY、BR_DEAD_REPLY 或 DEAD_OBJECT。此时先查 ref/node 解析,不要 先查服务实现。
代理死亡
BpBinder 在收到 DEAD_OBJECT 后将 mAlive 置为 false。旧代理不会自动绑定新服务;恢复必须由 上层重新执行服务发现并重建接口。
回调失败
回调对象被释放、服务线程退出或 oneway 队列无法继续时,原始同步调用可能已经成功;回调的 失败不是原始 reply 的失败。需要分别记录原请求 transaction 和回调 transaction。
9. 阅读边界
本文证明了本地短路、远程代理、node/ref 转换、同步事务栈和回调反转。没有展开死亡通知、 完整强弱引用释放、AIDL 生成代码的每个参数方向、RPC Binder 分支、线程池上限、SELinux 策略和 Binder allocator 细节。
可继续阅读:
- Binder调用源码地图:复习跨层同步事务主线;
- Linux IPC选择边界:比较 Binder 与其他 IPC 的 owner 和清理;
- 驱动层的 node/ref 专题:继续沿 binder_translate_binder() 和 binder_translate_handle();
- 事务协议专题:继续拆 BC/BR 命令和 transaction_stack。
最后做一个只读练习:任选一个通过 Parcel 传递的 Binder 对象,分别标出发送进程的 localBinder/ remoteBinder、目标进程的 node/ref、服务线程的 target/cookie 和 reply 的 transaction_stack。 如果某一步只能说“Binder 自动完成”,就回到对应源码的 owner 和锁边界。
10. 角色复述
同一事务中,A 发送请求时是 client,B 执行 onTransact 时是 server;若 B 使用 A 传来的 callback Binder,B 立即成为新的 client,A 的 callback 对象成为新的 server。驱动不保存“永久客户端/服务端”标签,只保存 transaction 的 from/to 和 node/ref 关系;角色随事务方向改变。
这也是本篇与 Binder调用源码地图 的边界:地图回答“跨哪些层”,本文回答“同一对象在不同事务里由谁消费”。具体嵌套栈和死锁条件留给 Binder嵌套调用。
