3、SurfaceFlinger启动流程:进程创建、主循环与消息处理、初始化关键步骤、VSync信号机制

各位同学,今天我们来聊聊SurfaceFlinger的启动流程。说实话,这块内容我当年啃源码的时候也花了不少时间。你想想看,一个负责整个屏幕合成的系统服务,它的启动过程必然藏着很多设计上的考量。我个人习惯把启动流程拆成四个部分来看:进程怎么来的、主循环怎么转的、初始化做了哪些关键事、VSync信号又是怎么产生的。

3.1 SurfaceFlinger进程创建

SurfaceFlinger不是普通App,它是一个系统服务。在Android系统中,它由init进程启动。具体来说,是在init.rc文件中定义的。

关键点:SurfaceFlinger是一个独立的native进程,不是binder线程池里的一个服务。

我们来看一下init.rc中的相关配置:

service surfaceflinger /system/bin/surfaceflinger
    class core
    user system
    group graphics drmrpc
    onrestart restart zygote
    writepid /dev/cpuset/system-background/tasks
    shutdown critical

嗯,这里要注意几个细节。第一,它属于core类,意味着系统核心服务启动时它就要起来。第二,它挂了会重启zygote——为什么?因为zygote依赖SurfaceFlinger做图形合成,SurfaceFlinger挂了,所有App界面都会卡死,不如一起重启。我在项目中遇到过几次SurfaceFlinger异常重启导致整机重启的情况,排查起来确实头疼。

进程启动后,main函数入口在main_surfaceflinger.cpp中:

int main(int, char**) {
    // 启动binder线程池
    ProcessState::self()->setThreadPoolMaxThreadCount(4);
    sp<ProcessState> ps(ProcessState::self());
    ps->startThreadPool();

    // 创建SurfaceFlinger实例
    sp<SurfaceFlinger> flinger = new SurfaceFlinger();

    // 初始化
    flinger->init();

    // 进入主循环
    flinger->run();

    return 0;
}

这段代码看起来简单,但每一步都藏着玄机。我建议你记住这个流程:创建实例 → 初始化 → 进入主循环。后面我们会逐一拆解。

3.2 主循环与消息处理

SurfaceFlinger的主循环不是简单的while(true),它基于Looper机制。说白了,它和Android App的Handler机制是同一套东西,只不过跑在native层。

我们看看SurfaceFlinger::run()的核心逻辑:

void SurfaceFlinger::run() {
    do {
        // 等待消息
        waitForEvent();
        
        // 处理消息
        if (CC_LIKELY(mStateDisplay->isPoweredOn())) {
            onMessageReceived(message);
        }
    } while (true);
}

这里有个细节我当年踩过坑。我曾经以为waitForEvent()就是简单的阻塞等待,后来发现它内部其实处理了VSync信号、Binder调用、定时器等多种事件源。它用的是Looper::pollOnce(),可以同时监听多个fd。

避坑指南:我曾经在调试一个显示延迟问题时,发现主循环被某个耗时操作卡住了。后来我养成了一个习惯——在关键路径上加ALOGD打印时间戳,这样能快速定位是哪个环节拖慢了帧率。

消息处理的核心是onMessageReceived(),它根据消息类型分发:

void SurfaceFlinger::onMessageReceived(int32_t what) {
    switch (what) {
        case MessageQueue::INVALIDATE:
            handleMessageInvalidate();
            break;
        case MessageQueue::REFRESH:
            handleMessageRefresh();
            break;
    }
}

INVALIDATE消息负责处理Layer状态的变更,比如App提交了新帧。REFRESH消息则负责真正的合成和显示。这两个消息的配合,构成了SurfaceFlinger的核心工作流。

3.3 初始化关键步骤

初始化函数SurfaceFlinger::init()做的事情非常多,我挑几个最重要的来说。

初始化步骤 作用 注意事项
创建HWComposer 与硬件显示控制器通信 不同芯片的HAL实现差异很大
创建EventThread 产生VSync信号 需要与DisplayHardware配合
初始化DisplayDevice 管理物理显示屏 支持多屏时要注意主屏选择
启动BootAnimation 显示开机动画 如果SurfaceFlinger启动慢,开机动画会卡

我个人觉得最关键的步骤是HWComposer的初始化。它直接决定了你的显示芯片能不能正常工作。我记得有一次移植到一款新平台,HWComposer初始化总是失败,最后发现是HAL层的接口版本不匹配。嗯,这种问题排查起来真的很费时间。

我们来看一下HWComposer初始化的核心代码:

void SurfaceFlinger::init() {
    // 1. 初始化HWComposer
    mHwc = new HWComposer(getFactory());
    if (mHwc->initCheck() != OK) {
        // 这里会直接导致SurfaceFlinger启动失败
        ALOGE("HWComposer initialization failed!");
        return;
    }

    // 2. 创建EventThread
    mEventThread = new EventThread(mHwc);
    mEventThread->run("EventThread", PRIORITY_URGENT_DISPLAY);

    // 3. 初始化DisplayDevice
    for (size_t i = 0; i < mHwc->getNumDisplays(); i++) {
        sp<DisplayDevice> display = new DisplayDevice(this, i);
        mDisplays.add(i, display);
    }

    // 4. 启动BootAnimation
    startBootAnimation();
}

警告:初始化顺序不能乱。比如EventThread必须在DisplayDevice之前创建,因为DisplayDevice需要注册VSync监听。我曾经见过有人把顺序搞反了,结果VSync信号一直收不到,屏幕刷新率直接掉到0。

3.4 VSync信号机制

VSync是SurfaceFlinger的心脏。没有VSync,整个显示系统就会乱套。你想想看,如果没有VSync,App可能在屏幕正在刷新的时候提交新帧,导致画面撕裂。

VSync信号的产生流程是这样的:

  1. 硬件产生垂直同步中断(VBlank interrupt)
  2. HWComposer的HAL层捕获这个中断
  3. EventThread从HWComposer读取VSync时间戳
  4. EventThread通知所有注册的监听者

我们来看看EventThread的核心逻辑:

bool EventThread::threadLoop() {
    // 等待VSync事件
    nsecs_t timestamp = mHwc->getVSyncTimestamp();
    
    if (timestamp > 0) {
        // 分发VSync给所有监听者
        for (auto& listener : mListeners) {
            listener->onVSyncReceived(timestamp);
        }
    }
    
    return true;
}

这里有个重要的设计:VSync信号是分发的,不是轮询的。每个监听者(比如App的Choreographer、SurfaceFlinger自己)都会注册到EventThread上。当VSync到来时,EventThread会依次通知它们。

个人经验:我曾经在调试一个帧率不稳的问题时,发现VSync信号的时间戳偶尔会跳变。后来定位到是HWComposer的HAL层实现有bug,在屏幕休眠唤醒后没有正确重置VSync计数器。这个坑让我学会了——永远不要完全信任硬件驱动,一定要在软件层做校验。

VSync信号在SurfaceFlinger内部有两个主要消费者:

  • INVALIDATE消息:收到VSync后,SurfaceFlinger会检查是否有新的Layer需要处理。如果有,就发送INVALIDATE消息给自己。
  • REFRESH消息:在INVALIDATE处理完成后,SurfaceFlinger会发送REFRESH消息,触发真正的合成和显示。

这个机制保证了每一帧的处理都在VSync的节奏下进行。说白了,就是让所有操作都同步到显示器的刷新周期上。

最后,我给大家留一个思考题:如果VSync信号的频率和显示器的刷新率不匹配,会发生什么?嗯,这个问题我在下一节课会详细讲,但你可以先想想看。