Binder同步调用
本文承接 Binder线程池创建、Binder优先级继承 和 AIDL后端生成。同步 Binder 调用的“阻塞”发生在用户态等待命令的循环中,而不是 writeTransactionData 写完就立即睡眠。本文把调用者、驱动、服务线程和 reply 四个 owner 串起来,并区分 BR_TRANSACTION_COMPLETE、BR_REPLY、死亡和失败回复。
1. 调用入口
源码文件:frameworks/native/libs/binder/BpBinder.cpp
BpBinder::transact 最终调用 IPCThreadState::transact。代理对象只拥有远端 handle;真正的等待状态属于发起调用的线程的 IPCThreadState,因此两个线程同时调用同一代理也各自阻塞。
2. 写入事务
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);
if (err != NO_ERROR) {
if (reply) reply->setError(err);
return (mLastError = err);
}
if ((flags & TF_ONE_WAY) == 0) {
err = reply ? waitForResponse(reply) : waitForResponse(&fakeReply);
}同步调用写入 BC_TRANSACTION 后立即进入 waitForResponse;写入失败不会进入等待。只有 TF_ONE_WAY 才跳过真实 reply,但仍调用等待循环以处理事务完成或冻结相关命令。
3. 驱动排队
源码文件:kernel/common/drivers/android/binder.c
驱动接收 BC_TRANSACTION,创建 binder_transaction,记录 sender、目标 node、flags 和数据 buffer。同步事务被挂到目标线程/进程工作队列;目标线程取到事务时,驱动向用户态输出 BR_TRANSACTION,并把非 oneway 事务压入目标线程的 transaction_stack。
服务线程随后在自己的 IPCThreadState::getAndExecuteCommand 中执行 Stub/BBinder。调用者线程并没有执行服务代码,它仍停留在自己的 waitForResponse,等待驱动返回命令。
4. 首次完成
源码文件:kernel/common/drivers/android/binder.c
驱动会为事务完成工作项输出 BR_TRANSACTION_COMPLETE。它只表示驱动已经接收并处理了发送方的 transaction 命令,不表示服务方法已经返回;同步调用者不能把这个命令当作业务 reply。
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
waitForResponse 收到 BR_TRANSACTION_COMPLETE 时,如果仍在等待真实 reply,继续循环;只有没有 reply buffer 的场景才可以结束等待。
5. 阻塞循环
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
while (1) {
if ((err = talkWithDriver()) < NO_ERROR) break;
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
cmd = (uint32_t)mIn.readInt32();
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
case BR_REPLY:
// 读取事务数据并 goto finish
}
}talkWithDriver() 在没有可读命令时通过 Binder fd 阻塞;驱动把目标服务的 reply 工作项放入调用线程队列并唤醒 fd 后,循环才继续。期间到达的其他命令会先由 executeCommand 处理,不能假设一次等待只会读到一个命令。
6. 服务回复
服务端完成方法后,IPCThreadState::sendReply 写入 BC_REPLY。驱动将 reply 事务绑定到原始同步事务的调用线程,向用户态输出 BR_REPLY,并从目标线程的 transaction_stack 弹出对应项。reply 的 data buffer 或 status code 随 binder_transaction_data 一起返回。
7. 解析结果
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
case BR_REPLY: {
binder_transaction_data tr;
err = mIn.read(&tr, sizeof(tr));
if ((tr.flags & TF_STATUS_CODE) == 0) {
reply->ipcSetDataReference(...);
} else {
err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
freeBuffer(...);
}
goto finish;
}普通 reply 把驱动 buffer 设置为 Parcel 数据引用;状态 reply 直接读取 status_t 并释放 buffer。waitForResponse 结束后,AIDL 生成的 Proxy 才继续解析返回值或抛出远程异常。
8. 失败结束
BR_DEAD_REPLY 映射为 DEAD_OBJECT,表示远端进程/线程死亡;BR_FAILED_REPLY 映射为 FAILED_TRANSACTION;冻结对象的 BR_FROZEN_REPLY 根据启用的错误码映射为 FROZEN_OBJECT 或 FAILED_TRANSACTION。这些命令都会跳到 finish,因此调用线程不会无限等待。
如果 talkWithDriver 返回负错误,或 mIn.errorCheck 发现输入损坏,也会离开循环。服务方法返回的业务错误则通常封装在 BR_REPLY 的 status code 中,不应与 Binder transport 失败混为一谈。
9. 事务栈
非 oneway 事务在发送方和接收方都维护 transaction_stack 关系。它支持嵌套同步调用、reply 路由和优先级保存恢复;若目标线程在处理事务时再次同步调用其他服务,内核通过 to_parent/from_parent 把内层 reply 送回正确的等待者。
10. 时间线
11. 阅读检查
从 BpBinder::transact 追到 waitForResponse,解释为什么 BR_TRANSACTION_COMPLETE 不能当业务返回值;再从服务端 BC_REPLY 追到调用端 Parcel 数据引用。最后分别代入服务进程死亡和事务状态错误,指出调用端得到的具体状态码。
