第3章:线程基本信息采集
SystemUI 无障碍线程的异常行为(如界面卡顿、输入无响应)往往源于线程数量膨胀、优先级错乱或关键线程阻塞。采集线程基本信息是定位问题的第一步,本章提供两种标准化采集方法:adb shell ps -T 快速扫描与 /proc/pid/task/ 深度解析。
3.1 现象识别
| 异常现象 | 线程层面征兆 | 判定规则 |
|---|---|---|
| 下拉通知栏卡顿 | 主线程(binder:xxx)在线程列表中长时间处于 R 状态 | 同一 TID 连续 3 次采样均为 R 状态且 CPU 占用 >30% |
| 无障碍焦点切换延迟 >200ms | AccessibilityInteractionClient 相关线程数量超过 5 个 | ps -T 输出中 grep “access” 线程数 >5 |
| SystemUI 全局 ANR | 某个子线程(如 AsyncTask #)处于 D 状态超过 5s | /proc/pid/task/*/stat 状态字段为 D 且累计时间 >5s |
3.2 数据采集
3.2.1 方法一:adb shell ps -T 快速扫描
使用 -T 选项列出指定进程下所有线程的 PID、TID、CPU 使用率、优先级及线程名。适用于现场初步判断线程规模与活跃度。
# 获取 SystemUI 进程 PID(假设为 1234)
adb shell ps -A | grep systemui | awk '{print $2}'
# 输出: 1234
# 获取线程列表
adb shell ps -T -p 1234
# 示例输出片段(简化):
# USER PID TID PPID VSZ RSS WCHAN ADDR S NAME
# u0_aXX 1234 1234 1 123456 45789 SyS_epo 0 S systemui
# u0_aXX 1234 1235 1 123456 45789 futex 0 S binder_io
# u0_aXX 1234 1240 1 123456 45789 do_sel 0 R Accessibility
# u0_aXX 1234 1245 1 123456 45789 pipe_w 0 D AsyncTask #1
经验规则:如果某线程 NAME 中包含 "Accessibility" 且状态为 R(运行)且 CPU% 超过 20%,极有可能正在执行高耗时无障碍回调。
3.2.2 方法二:/proc/[pid]/task/ 深度解析
进入进程目录,遍历每个 TID 子目录,读取 status、sched、stat、stack 等文件,获得线程精确状态、调度优先级、内核栈等底层信息。
# 遍历 /proc/1234/task/ 下的所有线程目录
adb shell "for d in /proc/1234/task/*/; do
echo '=== TID: '$(basename $d)' ==='
cat $d/status | grep -E '^(Name|State|Pid|Tgid|Ppid|Uid)'
cat $d/sched | head -3
echo '---'
done"
注意:
/proc/pid/task/ 中的 stack 文件只能在 root 权限下读取。若设备未解锁,可使用 adb shell cat /proc/1234/task/1245/stack 尝试,失败时返回空或 "permission denied"。
3.3 原因定位
使用故障树方法缩小嫌疑线程范围:
- 故障根:SystemUI 响应慢
- ├─ 主线程阻塞? → 检查 ps -T 中 NAME 为
systemui的 TID 状态是否为 D 或长时间 R - │ └─ 是 → 进一步读取 /proc/[pid]/task/[TID]/wchan 查看等待链
- ├─ 无障碍线程过载? → 统计 NAME 含 "Accessibility" 或 "assist" 的线程数量及 CPU%
- │ └─ 数量 >5 且总 CPU% >80% → 无障碍服务逻辑死循环或频繁反射
- └─ 子线程死锁? → grep 状态为 D 的线程,比对 stack 文件中的锁等待符号
3.4 修复动作
| 定位结论 | 修复动作 | 代码/命令参考 |
|---|---|---|
| 主线程因 binder 调用等待而阻塞 | 将 binder 同步调用改为异步,或使用 HandlerThread 解耦 |
|
| 无障碍线程数量异常(>10) | 限制无障碍服务线程池大小,复用已有线程 | |
| 某线程处于 D 状态超过 10s | 检查是否持有锁而未被释放,或内核驱动挂起 | |
3.5 复测验证
修复后重复执行数据采集命令,验证以下指标:
- 线程总数减少 ≤30%(与正常基线对比)
- 无障碍相关线程 CPU% 总和 <30%
- 无持续 D 状态线程存在
# 复测命令
adb shell ps -T -p $(adb shell ps -A | grep systemui | awk '{print $2}') | grep -E 'Accessibility|binder|AsyncTask'
# 期望输出:线程名清晰,状态以 S 为主,无 D 状态,CPU% 总和 < 30
3.6 预防方案
| 预防措施 | 具体实现 |
|---|---|
| 建立线程数量基线 | 在 CI/CD 流程中加入 adb shell ps -T 采集,若线程数超过历史平均值的 1.5 倍则报警 |
| 线程命名规范 | 所有无障碍相关 Thread 必须以 "Accessibility-" 前缀命名,便于筛选与归因 |
| 定期检查 /proc/pid/task/ 调度时间 | |
章节小结:通过
ps -T 与 /proc/pid/task/ 组合采集,可快速获取 SystemUI 无障碍线程的完整快照。结合状态、CPU% 和堆栈信息,能在 5 分钟内将问题线程范围缩小到 1-2 个嫌疑 TID。建议将本章命令封装为 sysui_thread_snapshot.sh 脚本,集成到日常性能巡检工具链中。