Skip to content

Binder死锁排查

基于 Binder 线程池、transaction_stack、用户态锁和调试输出,建立同步调用死锁的源码排查路径。

基于android-17.0.0_r1
AndroidBinder死锁调试

Binder死锁排查 ​

死锁排查的核心是区分线程池耗尽、同步调用环、用户锁环和单个慢服务。下图要求把 thread stack、transaction stack、锁等待和 async queue 放到同一决策树中。

本文承接 Binder嵌套调用、Binder线程耗尽排查、debugfs Binder节点 和 dumpsys分析Binder。Binder“卡住”可能是服务计算慢、线程池耗尽、同步调用环、用户态锁环或驱动协议错误。本文只用 Android 17 源码建立判别顺序,不把所有超时都命名为死锁。

1. 线程等待 ​

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

同步 IPCThreadState::transact 在写入 BC_TRANSACTION 后进入 waitForResponse;该循环在 talkWithDriver 中等待 BR_REPLY 或失败命令。线程栈停在这里只能证明“等待 reply”,不能证明服务端死锁;必须继续检查目标服务线程是否执行事务或同样等待下游。

2. 线程池耗尽 ​

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

debugfs 的 ready threads、requested threads、pending transaction 和 max_threads 可以判断是否没有可用 looper。BR_SPAWN_LOOPER 只有在无等待线程、无未响应请求且 started 小于 max 时才产生;如果所有线程都在同步等待,驱动不会凭空打破调用环。

3. 锁环 ​

用户态最常见场景是服务线程持有 mLock 调用 B,B 再同步回调 A 并申请同一把 mLock。Binder 驱动可以把回调送回 A 原线程(见 BD081),但 C++ std::mutex 不可重入,回调仍会阻塞。线程栈应同时显示锁等待和 waitForResponse,调用图要标出“持锁跨 IPC”边界。

4. withLock陷阱 ​

源码文件:frameworks/native/libs/binder/include/binder/IBinder.h

IBinder::withLock 的注释明确警告:在 Binder 内部锁保护的回调中调用大多数 IBinder 方法会 deadlock。该锁用于访问 attach object 等内部状态,不是业务同步原语;拿到对象后应在锁内完成局部修改,再释放锁执行任何 Binder 调用。

5. 嵌套调用环 ​

驱动通过 transaction_stack、from_parent、to_parent 路由嵌套 reply,但它只维护事务关系,不判断业务调用图是否有环。A→B→C→A 的同步链可能让每个进程各占一个线程;如果回调无法沿已有栈找到原线程,最终还会叠加线程池耗尽。

6. 驱动协议错误 ​

reply 必须对应当前线程 transaction_stack 顶部事务,目标线程不匹配会返回 BR_FAILED_REPLY/-EPROTO,而不是无限等待。debugfs transaction log 中有 return_error_line、return_error_param;看到这些字段时先检查坏栈/坏对象,再分析线程阻塞。

7. 阻塞服务 ​

服务方法可能在磁盘、网络、锁或下游同步 Binder 调用中长时间运行,但最终会返回。dumpsys 的 dump timeout、Binder transaction trace 时长和线程栈可以区分“慢”与“永不返回”;不要用一次超时样本直接断言死锁。

8. 真实复现 ​

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

ThreadPoolAvailableThreads 让服务持有 m_blockMutex,再并行发起 LOCK_UNLOCK 事务占满线程,最后调用 UNLOCK_AFTER_MS 释放锁;测试注释说明多一个调用会 deadlock,并验证等待至少持续指定 delay。这是可控的线程池/锁阻塞复现,不是驱动错误。

HangingServices 使用 PROCESS_TEMPORARY_LOCK 在服务端延迟解锁,随后并发调用并测量总耗时,证明服务线程被锁阻塞时客户端会同步等待,但在解锁后可以恢复。

9. 联合观测 ​

  1. 线程栈:waitForResponse、onTransact、mutex/condvar、下游 Binder。
  2. debugfs state/transactions:线程 looper、transaction stack、pending work。
  3. transaction log:debug id、from/to、nested、错误行。
  4. Binder trace:发送、接收、reply、buffer release 时间。
  5. 服务日志/dumpsys:业务锁、队列和下游依赖。

单独看 Java ANR 或客户端超时无法确定死锁 owner。

10. 判别矩阵 ​

现象更可能的 owner下一步
所有线程在 waitForResponse,事务栈形成环同步调用图检查回调和线程池
线程在同一 mutex,某线程持锁等待 Binder用户态业务锁消除持锁 IPC
ready=0,pending 增长,started=max线程池容量检查下游等待和 max 配置
async queue 增长,sync 正常单 node oneway检查消费/释放 buffer
log 有 -EPROTO/BR_FAILED_REPLY驱动协议/对象检查 transaction stack 和 Parcel
timeout 后最终恢复慢服务或暂时锁测量耗时,勿直接判死锁

11. 恢复原则 ​

修复调用图和锁顺序优先于盲目增加线程;把持锁期间的 IPC 改为锁外调用,或复制状态后异步通知;对可接受的通知使用 oneway,但为队列设置 backpressure;对真正大数据使用 fd/blob,避免 buffer 压力伪装成死锁。任何增加 max 的改动都要重新验证内存和嵌套调用上限。

12. 阅读检查 ​

给定客户端超时和服务端 8 个 Binder 线程,分别构造“服务计算 5 秒”“锁环”“A→B→C→A 同步环”“oneway 队列堆积”四种解释,并为每种指定线程栈、debugfs 字段、transaction log 和恢复动作。能指出 owner,而不是只说“Binder 卡住”,才完成死锁排查。