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的完整流程
好,我们把整个流程串起来看一遍:
- Zygote启动:init进程启动Zygote,Zygote预加载Framework类和资源。
- 等待指令:Zygote进入
runSelectLoop(),监听Socket。 - 收到启动SystemServer指令:由
startSystemServer()方法发起。 - fork进程:调用
forkSystemServer(),创建子进程。 - 子进程初始化:子进程调用
handleSystemServerProcess()。 - RuntimeInit.zygoteInit():初始化Java环境,包括Binder线程池。
- 抛出MethodAndArgsCaller:清理调用栈。
- SystemServer.main():正式进入SystemServer的世界。
你想想看,这个过程设计得其实很精妙。Zygote作为“母体”,fork出SystemServer后,SystemServer就独立了。它有自己的进程空间、自己的Binder线程池,完全脱离了Zygote的控制。
我记得有一次,我们需要在SystemServer启动前做一些定制化的初始化。当时我就在 handleSystemServerProcess() 和 RuntimeInit.zygoteInit() 之间插了个钩子。这个位置刚好是native和Java的交界处,做系统级hook非常合适。
嗯,这一章的内容就到这里。下一章我们会深入SystemServer内部,看看它到底是怎么把AMS、WMS这些核心服务一个个启动起来的。到时候你会发现,SystemServer的启动顺序其实藏着很多设计哲学。