23、图形系统稳定性:HWC crash恢复、GPU驱动异常处理、SurfaceFlinger watchdog
图形系统崩溃,说实话,是Android系统里最让人头疼的问题之一。
为什么?因为一旦出问题,用户看到的就是黑屏、花屏、冻屏。你没法优雅地弹个对话框说「抱歉,图形服务挂了」。我经历过不少这样的线上事故,大半夜被叫起来查问题,那种感觉,嗯,你懂的。
这一章,我们就来聊聊图形系统的三大稳定性防线:HWC crash恢复、GPU驱动异常处理、SurfaceFlinger watchdog。这三块搞定了,你的系统才算真正「扛造」。
23.1 HWC crash恢复:别让合成器拖垮整个系统
HWC(Hardware Composer)是硬件合成模块。它一旦挂了,SurfaceFlinger也跟着遭殃。说白了,HWC crash就是合成器进程异常退出或通信中断。
23.1.1 HWC crash的典型场景
- HWC进程异常退出:比如空指针、内存越界,直接SIGSEGV
- HIDL服务死亡:HWC作为HIDL服务,binder死亡通知没处理好
- 硬件超时:合成操作卡在硬件层,超过阈值没返回
核心思路:HWC crash后,SurfaceFlinger不能直接崩。要能降级到GPU合成,或者重启HWC服务。
23.1.2 我建议的恢复策略
我在项目中遇到过HWC频繁crash的情况,后来总结了一套恢复流程:
- 检测死亡:SurfaceFlinger监听HWC的HIDL死亡通知
- 降级合成:立即切到GPU合成(GLES合成),保证画面还能输出
- 尝试重启:重新拉起HWC服务,重新初始化
- 恢复合成:HWC重启成功后,切回硬件合成
// SurfaceFlinger中HWC死亡回调示例
void onHwcDied() {
ALOGE("HWC died! Switching to GPU composition");
mUseHwc = false; // 降级到GPU合成
mHwcRestartThread->requestRestart(); // 异步重启HWC
invalidate(); // 触发重绘
}
避坑指南:我曾经在重启HWC时直接阻塞了主线程,结果SurfaceFlinger watchdog先触发了。记住,重启操作一定要异步,别在合成线程里干等。
23.2 GPU驱动异常处理:别让驱动把整个系统拖下水
GPU驱动异常,说白了就是驱动层出了幺蛾子。可能是驱动bug,也可能是GPU硬件hang住了。
23.2.1 GPU驱动的常见异常
| 异常类型 | 表现 | 根因 |
|---|---|---|
| GPU hang | 画面卡死,log中看到GPU timeout | 着色器编译死循环、硬件bug |
| 驱动崩溃 | dmesg中有kernel panic或oops | 驱动代码bug、内存越界 |
| 内存泄漏 | GPU内存持续增长,最终分配失败 | 纹理/缓冲区未释放 |
23.2.2 我的处理经验
我记得有一次,GPU驱动在特定游戏场景下频繁hang。查了很久才发现是某个着色器优化过头了。
处理GPU异常,我建议分三步走:
- 检测:通过drm或kgsl接口监控GPU状态,超时即判定异常
- 恢复:尝试GPU reset,重置硬件状态
- 降级:如果reset失败,切到CPU渲染,虽然慢但至少不黑屏
// GPU hang检测伪代码
void checkGpuHealth() {
int timeout = readGpuRegister(GPU_TIMEOUT_REG);
if (timeout > GPU_HANG_THRESHOLD) {
ALOGW("GPU可能hang了,尝试reset");
if (gpuReset() != SUCCESS) {
ALOGE("GPU reset失败,降级到CPU渲染");
switchToCpuRendering();
}
}
}
注意:GPU reset操作很危险,搞不好会连带其他进程崩溃。我建议reset前先保存关键状态,reset失败后直接走降级路径,别死磕。
23.3 SurfaceFlinger watchdog:最后的防线
SurfaceFlinger是图形系统的核心。它一旦卡死,整个UI就停了。Watchdog就是用来检测这种情况的。
23.3.1 Watchdog的工作原理
说白了,就是定时检查SurfaceFlinger的主线程是否还在正常运转。如果超过阈值没响应,就认为它卡死了。
// SurfaceFlinger watchdog简化实现
void WatchdogThread::run() {
while (mRunning) {
sleep(mTimeoutSec); // 比如5秒
if (!mMainThreadResponded) {
ALOGE("SurfaceFlinger主线程无响应!触发dump");
dumpState();
// 可以尝试重启SurfaceFlinger
restartSurfaceFlinger();
}
mMainThreadResponded = false;
}
}
// 主线程定期喂狗
void SurfaceFlinger::onMessageReceived() {
mWatchdog->feed(); // 告诉watchdog我还活着
// ... 处理消息
}
23.3.2 我踩过的坑
我曾经遇到过一个问题:watchdog超时了,但SurfaceFlinger其实没死,只是某个合成操作耗时太长。结果watchdog误判,把SurfaceFlinger给重启了,用户看到闪了一下黑屏。
后来我加了一个判断:如果主线程还在处理消息,只是处理得慢,那就多给点时间,别急着重启。
我的建议:watchdog的超时时间别设太短。我一般设8-10秒。太短容易误杀,太长又起不到保护作用。另外,dump信息一定要完整,方便事后分析。
23.4 知识体系总览
下面这张图,是我梳理的图形系统稳定性知识体系。你可以把它当作一个检查清单。
23.5 总结
图形系统稳定性,说白了就是三件事:HWC挂了能恢复,GPU异常能兜底,SurfaceFlinger卡了能自救。
你想想看,用户最怕的是什么?是黑屏。只要画面还能动,哪怕掉帧、卡顿,用户都能忍。但一旦黑屏,那就是灾难。
所以我的原则是:宁可降级,不可崩溃。GPU合成不行就CPU渲染,硬件合成不行就软件合成。总之,画面不能停。
嗯,这一章的内容就到这里。记住,稳定性不是靠运气,是靠每一行代码的防御性设计。
公众号:蓝海资料掘金营,微信deep3321