Skip to content

Binder同步调用

从 BC_TRANSACTION 到 BR_REPLY,追踪 Binder 同步调用的阻塞、唤醒、事务栈和失败返回路径。

基于android-17.0.0_r1
AndroidBinderIPC

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

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

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

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 数据引用。最后分别代入服务进程死亡和事务状态错误,指出调用端得到的具体状态码。