2、SystemServer的启动流程:从Zygote到SystemServer的fork过程、RuntimeInit与ZygoteInit的调用链

好,咱们接着往下聊。上一章我们讲了Zygote是怎么启动的,它像个孵化器一样等着干活。那这一章,我们就来看看Zygote孵化出的第一个、也是最重要的一个进程——SystemServer。

说白了,SystemServer就是Android系统的“大管家”。AMS、WMS、PMS这些核心服务,全都在它肚子里。Zygote fork出SystemServer之后,整个Android世界才算真正活过来。

2.1 Zygote的fork动作:从等待到行动

Zygote启动后,会进入一个死循环,监听Socket。一旦收到“孵化”指令,它就调用 ZygoteConnection.processOneCommand() 来处理。

我带你看看关键代码。在 ZygoteConnection.java 里,核心逻辑是这样的:

// ZygoteConnection.java
Runnable processOneCommand(ZygoteServer zygoteServer) {
    String[] args = readArgumentList();
    // 解析参数,比如"--setuid=1000"、"--setgid=1000"
    // 还有最重要的"--nice-name=system_server"
    ...
    // 关键一步:fork
    pid = Zygote.forkAndSpecialize(
            uid, gid, gids, runtimeFlags, rlimits,
            seInfo, niceName, fdsToClose, fdsToIgnore,
            instructionSet, appDataDir);
    
    if (pid == 0) {
        // 子进程(也就是SystemServer)走这里
        zygoteServer.closeServerSocket();
        return handleChildProc(parsedArgs, ...);
    } else {
        // 父进程(Zygote)走这里
        return null;
    }
}

嗯,这里要注意。Zygote fork出SystemServer时,用的是 forkAndSpecialize(),而不是 forkSystemServer()。这两个方法有什么区别?

forkSystemServer() 是专门为SystemServer准备的,它会做一些额外的初始化,比如设置进程的nice name为“system_server”。而 forkAndSpecialize() 是给普通应用进程用的。但你看源码会发现,SystemServer的fork其实走的是 forkSystemServer() 路径。

关键点:Zygote fork SystemServer时,会保留Zygote的地址空间和已加载的类。这就是为什么SystemServer能直接使用Zygote中预加载的Framework类,省去了重复加载的时间。

2.2 RuntimeInit的调用链:从native到Java的桥梁

fork完成后,子进程会调用 handleChildProc()。这个方法最终会走到 RuntimeInit.zygoteInit()

我个人习惯把RuntimeInit看作是“Java世界的初始化器”。它负责把native环境切换到Java环境,然后启动Java代码。

// RuntimeInit.java
public static final void zygoteInit(int targetSdkVersion, String[] argv,
        ClassLoader classLoader) throws ZygoteInit.MethodAndArgsCaller {
    
    // 1. 重定向log输出
    redirectLogStreams();
    
    // 2. 通用初始化(时区、键盘、用户目录等)
    commonInit();
    
    // 3. native层的Zygote初始化
    nativeZygoteInit();
    
    // 4. 进入SystemServer的main方法
    applicationInit(targetSdkVersion, argv, classLoader);
}

你看,这个调用链很清晰。但有个细节我当年看源码时困惑了很久——为什么最后一步是 applicationInit(),而不是直接调用SystemServer.main()?

答案在 applicationInit() 里:

// RuntimeInit.java
private static void applicationInit(int targetSdkVersion, String[] argv,
        ClassLoader classLoader) throws ZygoteInit.MethodAndArgsCaller {
    
    // 找到要执行的类的main方法
    // 对于SystemServer来说,就是"com.android.server.SystemServer"
    RuntimeInit.Arguments args = new RuntimeInit.Arguments(argv);
    
    // 通过反射调用main方法
    invokeStaticMain(args.startClass, args.startArgs, classLoader);
}

这里用了一个巧妙的设计——invokeStaticMain() 会抛出一个 MethodAndArgsCaller 异常。为什么不用普通的方法调用?

我的理解:这样做是为了清理Zygote的调用栈。因为Zygote fork出来的子进程,栈上还残留着Zygote的调用帧。通过抛出异常,让ZygoteInit的main方法在catch块里重新调用SystemServer.main(),这样栈就干净了。我在项目中排查过一些奇怪的栈溢出问题,最后发现就是调用栈太深导致的。

2.3 ZygoteInit的收尾工作:清理与启动

回到 ZygoteInit.java,它的main方法长这样:

// ZygoteInit.java
public static void main(String[] argv) {
    ...
    // 启动SystemServer
    Runnable caller = zygoteServer.runSelectLoop(abiList);
    
    if (caller != null) {
        caller.run();
    }
}

runSelectLoop() 返回一个Runnable时,就说明有子进程需要启动。这个Runnable其实就是 MethodAndArgsCaller 异常里封装好的调用。

我带你看看完整的调用链:

步骤 调用方 方法 说明
1 ZygoteInit.main() runSelectLoop() 等待Socket指令
2 ZygoteConnection processOneCommand() 解析参数,fork进程
3 子进程 handleChildProc() 子进程初始化
4 RuntimeInit zygoteInit() Java环境初始化
5 RuntimeInit applicationInit() 找到main方法
6 RuntimeInit invokeStaticMain() 抛出MethodAndArgsCaller
7 ZygoteInit.main() catch块 调用caller.run()
8 SystemServer main() 正式启动

这个调用链,说白了就是一次“接力跑”。ZygoteInit把棒子交给RuntimeInit,RuntimeInit准备好跑道后,再通过异常把棒子传回ZygoteInit,最后由ZygoteInit把棒子交给SystemServer。

曾经踩过的坑:我在做系统优化时,发现SystemServer启动偶尔会卡住。最后定位到是 nativeZygoteInit() 里做了太多初始化工作。这个方法是native层的,负责启动Binder线程池。如果Binder驱动有问题,整个SystemServer就卡在native层了。所以后来我们加了个超时机制。

2.4 从Zygote到SystemServer的完整流程

好,我们把整个流程串起来看一遍:

  1. Zygote启动:init进程启动Zygote,Zygote预加载Framework类和资源。
  2. 等待指令:Zygote进入 runSelectLoop(),监听Socket。
  3. 收到启动SystemServer指令:由 startSystemServer() 方法发起。
  4. fork进程:调用 forkSystemServer(),创建子进程。
  5. 子进程初始化:子进程调用 handleSystemServerProcess()
  6. RuntimeInit.zygoteInit():初始化Java环境,包括Binder线程池。
  7. 抛出MethodAndArgsCaller:清理调用栈。
  8. SystemServer.main():正式进入SystemServer的世界。

你想想看,这个过程设计得其实很精妙。Zygote作为“母体”,fork出SystemServer后,SystemServer就独立了。它有自己的进程空间、自己的Binder线程池,完全脱离了Zygote的控制。

我记得有一次,我们需要在SystemServer启动前做一些定制化的初始化。当时我就在 handleSystemServerProcess()RuntimeInit.zygoteInit() 之间插了个钩子。这个位置刚好是native和Java的交界处,做系统级hook非常合适。

嗯,这一章的内容就到这里。下一章我们会深入SystemServer内部,看看它到底是怎么把AMS、WMS这些核心服务一个个启动起来的。到时候你会发现,SystemServer的启动顺序其实藏着很多设计哲学。