深入解析 Binder 运行的状态

133 阅读16分钟

当出现应用卡顿等性能问题时,如何通过查看 Binder 的运行时状态来诊断你的应用?本文将展示了一个简单实用的手段

0x00. 查看 Binder 实体运行状态

  1. 环境准备: 需要一台 Root 过的手机或模拟器。
  2. 执行命令: 输出数据量比较大,这里保存到一个文件中
# 这个命令会列出系统中所有进程的binder状态
adb shell cat /sys/kernel/debug/binder/state > binder.txt
# 可以指定进程 PID 查看具体进程的binder状态
# adb shell cat /sys/kernel/debug/binder/proc/3011
  1. 分析重点: 在输出的巨量文本中,找到你正在开发的 App PID(本文PID=3011)。
  • proc 节点:它开了多少个线程(threads)?
  • nodes:它暴露了多少个 Binder 实体(服务)给别人用?
  • refs:它引用了多少个远程服务?

输出的 binder.txt 内容实在太大,这里只展示PID:3011(包名为com.android.launcher3)的binder 运行状态

proc 3011
context binder
  thread 3011: l 00 need_return 0 tr 0
  thread 3060: l 12 need_return 0 tr 0
  thread 3061: l 11 need_return 0 tr 0
  thread 3062: l 11 need_return 0 tr 0
  thread 3136: l 00 need_return 0 tr 0
  thread 3196: l 00 need_return 0 tr 0
  thread 3198: l 00 need_return 0 tr 0
  thread 3203: l 11 need_return 0 tr 0
  thread 3209: l 00 need_return 0 tr 0
  thread 3210: l 00 need_return 0 tr 0
  node 30941: ub400006dcd0c4b20 cb400006dfd0981d0 pri 0:139 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 tr 1 proc 1411
  node 31046: ub400006dcd0d70f0 cb400006dfd0988f0 pri 0:139 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 tr 1 proc 1411
  node 33583: ub400006dcd0f07a0 cb400006dfd0ac930 pri 0:139 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 tr 1 proc 1411
  node 63104: ub400006dcd0f2060 cb400006e2d120db8 pri 0:139 hs 1 hw 1 ls 1 lw 1 is 0 iw 0 tr 1
  node 33482: ub400006dcd0f4f40 cb400006ded0b0d30 pri 0:139 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 tr 1 proc 581
  node 32258: ub400006dcd0f7cd0 cb400006dfd09ee30 pri 0:139 hs 1 hw 1 ls 0 lw 0 is 2 iw 2 tr 1 proc 2643 1411
  node 32218: ub400006dcd0f7d60 cb400006dfd0a06f0 pri 0:139 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 tr 1 proc 1411
  node 32256: ub400006dcd0f7e80 cb400006dfd09fcd0 pri 0:139 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 tr 1 proc 1411
  node 32262: ub400006dcd0f7eb0 cb400006dfd0969d0 pri 0:139 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 tr 1 proc 1411
  node 32295: ub400006dcd0f7ee0 cb400006dfd09f310 pri 0:139 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 tr 1 proc 1411
  node 32308: ub400006dcd0f8120 cb400006dfd09f370 pri 0:139 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 tr 1 proc 1411
  node 32091: ub400006dcd0f8240 cb400006dfd09f010 pri 0:139 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 tr 1 proc 1411
  node 31614: ub400006dcd102d10 cb400006dfd09e950 pri 0:139 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 tr 1 proc 1411
  ref 30933: desc 0 node 1 s 1 w 1 d 0000000000000000
  ref 30936: desc 1 node 3466 s 1 w 1 d 0000000000000000
  ref 30955: desc 2 node 2917 s 1 w 1 d 0000000000000000
  ref 30956: desc 3 node 2714 s 1 w 1 d 0000000000000000
  ref 30957: desc 4 node 4037 s 1 w 1 d 0000000000000000
  ref 30958: desc 5 node 8172 s 1 w 1 d 0000000000000000
  ref 30959: desc 6 node 3446 s 1 w 1 d 0000000000000000
  ref 30960: desc 7 node 4200 s 1 w 1 d 0000000000000000
  ref 30962: desc 8 node 4418 s 1 w 1 d 0000000000000000
  ref 30963: desc 9 node 3462 s 1 w 1 d 0000000000000000
  ref 30964: desc 10 node 4150 s 1 w 1 d 0000000000000000
  ref 30965: desc 11 node 4205 s 1 w 1 d 0000000000000000
  ref 30966: desc 12 node 4519 s 1 w 1 d 0000000000000000
  ref 30967: desc 13 node 2775 s 1 w 1 d 0000000000000000
  ref 30968: desc 14 node 6037 s 1 w 1 d 0000000000000000
  ref 30969: desc 15 node 4041 s 1 w 1 d 0000000000000000
  ref 30970: desc 16 node 4411 s 1 w 1 d 0000000000000000
  ref 30971: desc 17 node 8081 s 1 w 1 d 0000000000000000
  ref 30972: desc 18 node 4124 s 1 w 1 d 0000000000000000
  ref 30973: desc 19 node 2860 s 1 w 1 d 0000000000000000
  ref 30974: desc 20 node 5801 s 1 w 1 d 0000000000000000
  ref 30975: desc 21 node 4878 s 1 w 1 d 0000000000000000
  ref 30992: desc 22 node 30991 s 1 w 1 d 0000000000000000
  ref 30994: desc 23 node 30993 s 1 w 1 d 0000000000000000
  ref 31597: desc 24 node 571 s 1 w 1 d 0000000000000000
  ref 31601: desc 25 node 31600 s 1 w 1 d 0000000000000000
  ref 31819: desc 26 node 31818 s 1 w 1 d 0000000000000000
  ref 31757: desc 27 node 2168 s 1 w 1 d 0000000000000000
  ref 31771: desc 28 node 999 s 1 w 1 d 0000000000000000
  ref 31989: desc 29 node 9401 s 1 w 1 d 0000000000000000
  ref 31991: desc 30 node 31990 s 1 w 1 d 0000000000000000
  ref 32266: desc 31 node 32265 s 1 w 1 d 0000000000000000
  ref 32270: desc 32 node 32269 s 1 w 1 d 0000000000000000
  ref 32339: desc 33 node 32338 s 1 w 1 d 0000000000000000
  ref 32569: desc 34 node 8313 s 1 w 1 d 0000000000000000
  ref 32577: desc 35 node 8319 s 1 w 1 d 0000000000000000
  ref 33004: desc 36 node 32318 s 1 w 1 d 0000000000000000
  ref 33005: desc 37 node 32981 s 1 w 1 d 0000000000000000
  ref 33006: desc 38 node 32983 s 1 w 1 d 0000000000000000
  ref 33023: desc 39 node 18749 s 1 w 1 d 0000000000000000
  ref 78180: desc 40 node 78169 s 1 w 1 d 0000000000000000
  ref 61925: desc 41 node 61900 s 1 w 1 d 0000000000000000
  ref 33739: desc 42 node 33725 s 1 w 1 d 0000000000000000
  ref 33740: desc 43 node 33710 s 1 w 1 d 0000000000000000
  ref 33858: desc 44 node 25540 s 1 w 1 d 0000000000000000
  ref 33859: desc 45 node 33759 s 1 w 1 d 0000000000000000
  buffer 33519: 0000000000000000 size 8:0:0 delivered
  buffer 63108: 0000000000000000 size 0:0:0 delivered
  buffer 32963: 0000000000000000 size 36:0:0 delivered
  buffer 63164: 0000000000000000 size 144:0:0 delivered

0x01. 线程池状态分析(Threads)

这里列出了 10 个线程(从 3011 到 3210)。

  • 关键数据: l 值(代表 looper 状态)。

    • l 11 (BC_REGISTER_LOOPER) 和 l 12 (BC_ENTER_LOOPER) 表示这些线程是 Binder 工作线程
    • thread 3060, 3061, 3062, 3203 这四个线程目前处于 Ready 状态,等待任务。
  • 专家视角: * 健康度: 10 个线程远未达到默认的 15 个上限,说明该进程目前没有 Binder 线程饥饿问题。

    • 主线程: thread 3011 是主线程(PID=TID),它的 tr 0 表示目前没有正在进行的 Binder 事务。如果这里 tr 长期不为 0,说明主线程被 Binder 调用卡住了(即常见的 ANR 隐患)。

0x02. Binder 实体分析(Nodes)—— 你暴露了什么?

Node 代表进程 3011 提供的接口(Stub)。

  • 多端引用: 看到 node 32258 后面跟着 proc 2643 1411

    • 这说明进程 2643 和 1411 都在持有并可能调用 3011 的同一个接口。
  • 跨进程足迹: 3011 暴露的 Node 大部分被 PID 1411 引用。

    • 专家行动: 可以去查一下 PID 1411 是哪个进程(通常是 system_server 或关键系统 UI 进程)。这能帮你理清你的 App 主要是被谁在频繁调用。

0x03. 引用分析(Refs)—— 你持有了谁?

Ref 代表进程 3011 调用的远程接口(Proxy)。

  • 句柄 0 (desc 0): ref 30933: desc 0 node 1。这是标准的 ServiceManager

  • 引用数量: 你拥有从 desc 0desc 45 的引用。

    • 专家视角: 45 个外部引用对于一个 App 进程来说是正常的。但如果 desc 达到几百,说明你的 App 申请了太多的系统服务(如频繁获取 LocationManager, WindowManager 等)且没有释放,这会导致 系统服务端 出现内存泄漏。

0x04. 内存缓冲区(Buffers)—— 风险核心

这是最硬核的部分,直接关联到 TransactionTooLargeException

  • 数据: buffer 63164: size 144:0:0 delivered

  • 解析: * 144:0:0 分别代表:数据大小 : 偏移量大小 : 死亡通知大小

    • 这里的 Buffer 都很小(最大才 144 字节),且状态都是 delivered(已交付)。
  • 预警信号: 如果你在排查卡顿或崩溃时,看到某个 buffersize 达到 500,000 以上(约 0.5MB),并且状态不是 delivered 而是长时间挂起,说明有一个大事务阻塞了 Binder 驱动,这会直接导致后续所有简单的 Binder 调用超时。

0x05. 寻找“幕后黑手”

  1. 从查询 PID
adb shell ps -A | grep 1411                                                        

system  1411    851 16778868 399132 do_epoll_wait      0 S system_server

adb shell ps -A | grep 2643                                                        

u0_a118  2643    851 13886788 123388 do_epoll_wait      0 S com.android.inputmethod.latin

adb shell ps -A | grep 581                                                         

system    581      1 11133444 43892 do_epoll_wait       0 S surfaceflinger

可以发现这个进程(com.android.launcher3)主要是跟 system_server、输入法、surfaceflinger 进程交互。

  1. 思考: 为什么 node 63104 只有 ls 1 lw 1(本地强弱引用)而没有 proc 列表?
    答:这通常意味着该对象刚创建或正在销毁,尚未被远程进程获取

0x06. 还有别的方式

adb shell cat /d/binder/stats

这个指令也会把系统所有的进程的Binder状态进行统计输出

proc 3011 context binder threads: 10 # 当前进程已启动了 10 个 Binder 线程。 
requested threads: 0+3/15 ready threads 4 # 关键指标! 此时有 4 个线程正处于空闲状态,随时可以处理新的请求。这说明进程没有发生 Binder 线程枯竭。 
free async space 520192 
nodes: 13 refs: 46 s 46 w 46 buffers: 4 
pages: 1:7:246 # 分别表示已分配的物理页、未使用的页、以及从未被访问过的页数量 
pages high watermark: 8 # 该进程 Binder 内存使用的峰值为 8 个物理页(约 32 KB)。这说明该进程历史上处理的数据量其实非常小。 
pending transactions: 0 # 关键指标! 当前没有排队等待的事务。如果这个数字很大,说明系统已经卡死了。 
BC_TRANSACTION: 1020 
BC_REPLY: 8 
BC_FREE_BUFFER: 923 
BC_INCREFS: 62 
BC_ACQUIRE: 62 
BC_RELEASE: 16 
BC_DECREFS: 16 
BC_INCREFS_DONE: 66 
BC_ACQUIRE_DONE: 67 
BC_REGISTER_LOOPER: 3 
BC_ENTER_LOOPER: 1 
BC_REQUEST_DEATH_NOTIFICATION: 1 
BR_TRANSACTION: 81 
BR_REPLY: 846 
BR_TRANSACTION_COMPLETE: 1028 
BR_INCREFS: 67 
BR_ACQUIRE: 68 
BR_RELEASE: 55 
BR_DECREFS: 54 
BR_SPAWN_LOOPER: 3

以下是各项关键指标的详细解析:

1. 线程池状态 (Thread Pool)

这是判断进程是否卡顿的最直接依据。

  • threads: 10: 当前进程已启动了 10 个 Binder 线程。

  • requested threads: 0+3/15:

    • 0: 当前没有正在请求但未启动的线程。
    • 3: 历史上一共请求启动过 3 个线程(对应下方的 BC_REGISTER_LOOPER)。
    • 15: 该进程定义的线程池最大上限(通常为 15,加上主线程共 16 个)。
  • ready threads 4: 关键指标! 此时有 4 个线程正处于空闲状态,随时可以处理新的请求。这说明进程没有发生 Binder 线程枯竭。

2. 内存与缓冲区 (Buffer & Memory)

  • free async space 520192:

    • 异步(Oneway)调用的剩余缓冲区大小。
    • 计算:520192/1024=508KB520192 / 1024 = 508 KB
    • 分析:Binder 总缓冲区约为 1MB,异步调用通常限制使用其中的一半。由于还剩约 500KB,说明目前没有大型异步任务(如传递巨型图片或频繁的异步回调)积压。
  • pages: 1:7:246: 分别表示已分配的物理页、未使用的页、以及从未被访问过的页数量。

  • pages high watermark: 8: 该进程 Binder 内存使用的峰值为 8 个物理页(约 32KB32 KB)。这说明该进程历史上处理的数据量其实非常小。

3. 对象引用 (Nodes & Refs)

  • nodes: 13: 该进程作为“服务端”,向外暴露了 13 个 Binder 实体对象。

  • refs: 46 s 46 w 46: 该进程作为“客户端”,持有了 46 个远程 Binder 对象的引用。

    • s (Strong): 强引用计数。
    • w (Weak): 弱引用计数。

4. 事务统计 (Commands & Protocols)

这部分展示了用户态(App)与内核态(Driver)之间的交互次数。

  • pending transactions: 0: 关键指标! 当前没有排队等待的事务。如果这个数字很大,说明系统已经卡死了。

  • BC_TRANSACTION: 1020 vs BC_REPLY: 8:

    • 说明该进程主要是“发起方”。它发出了 1020 次请求。
    • 只有 8 次是作为服务端回复给别人的,这表明该进程更像是一个 Client 端。
  • BR_SPAWN_LOOPER: 3: 内核曾 3 次告诉应用:“你的人手不够了,请再开一个 Binder 线程”。这与上面的 requested threads: 3 相吻合。

4.诊断结论

PID 3011 运行状况良好:

  1. 无阻塞ready threads 为 4,且 pending transactions 为 0。
  2. 负载低:内存水位线(watermark)仅为 8 页,缓冲区剩余空间充裕。
  3. 行为特征:这是一个典型的“重客户端”进程。它频繁调用其他服务(BC_TRANSACTION 高达 1020 次),但自身很少被别人调用。