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的情况,后来总结了一套恢复流程:

  1. 检测死亡:SurfaceFlinger监听HWC的HIDL死亡通知
  2. 降级合成:立即切到GPU合成(GLES合成),保证画面还能输出
  3. 尝试重启:重新拉起HWC服务,重新初始化
  4. 恢复合成: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 知识体系总览

下面这张图,是我梳理的图形系统稳定性知识体系。你可以把它当作一个检查清单。

图形系统稳定性知识体系 HWC Crash恢复 GPU驱动异常处理 SurfaceFlinger Watchdog 检测HIDL死亡通知 降级到GPU合成 异步重启HWC服务 恢复硬件合成 监控GPU超时状态 尝试GPU reset 降级到CPU渲染 驱动内存泄漏检测 定时检查主线程 超时触发dump 避免误杀(慢处理) 重启SurfaceFlinger 目标:黑屏/花屏时间 < 2秒,用户无感知

23.5 总结

图形系统稳定性,说白了就是三件事:HWC挂了能恢复,GPU异常能兜底,SurfaceFlinger卡了能自救。

你想想看,用户最怕的是什么?是黑屏。只要画面还能动,哪怕掉帧、卡顿,用户都能忍。但一旦黑屏,那就是灾难。

所以我的原则是:宁可降级,不可崩溃。GPU合成不行就CPU渲染,硬件合成不行就软件合成。总之,画面不能停。

嗯,这一章的内容就到这里。记住,稳定性不是靠运气,是靠每一行代码的防御性设计。


公众号:蓝海资料掘金营,微信deep3321