Android-Framework(四)-开机启动SystemServer进程

文章内容

一、从 Zygote 到系统服务

Zygote 完成预加载后,Android 还不能立即运行应用。管理 Activity、窗口、电源和软件包的服务需要先启动,而这些服务中的许多都运行在同一个进程中:system_server。

理解这段启动流程,要分清三个层次:

  • 进程创建:Zygote 通过 fork() 创建 system_server,为子进程设置运行身份。
  • 运行环境初始化:子进程加载入口类、启动 Binder 线程池,进入 SystemServer.main()。
  • 服务初始化与发布:SystemServer 按依赖顺序创建服务,并把需要跨进程访问的 Binder 接口注册到 servicemanager。

system_server 表示进程,SystemServer 则为该进程的 Java 入口类。

开机到 Zygote、SystemServer 与应用进程的全局地图

init 启动 Zygote,Zygote 再创建 system_server 和应用进程。支持两种 ABI 的设备可能同时运行主、次两个 Zygote;负责启动 system_server 的是带有相应启动参数的那个 Zygote。Android 还有由 init 直接启动的原生进程,因此不能把 Zygote 理解为所有用户空间进程的父进程。

二、启动入口:Zygote 为什么先创建 system_server

init 的 Zygote 服务配置会向 app_process 传入 --start-system-server。

相关控制流的简化代码:

java
// 代码位置 /frameworks/base/core/java/com/android/internal/os/ZygoteInit.java 第 565—622 行// ZygoteInit.main():省略其余参数解析与初始化boolean startSystemServer = false;for (int i = 1; i < argv.length; i++) {    if ("start-system-server".equals(argv[i])) {        startSystemServer = true;    }} registerZygoteSocket(socketName);preload();// 省略预加载后的清理等工作 if (startSystemServer) {    startSystemServer(abiList, socketName);} runSelectLoop(abiList);

这里最重要的是执行顺序:先预加载,再 fork 出 system_server,父进程随后进入 socket 监听循环。

创建 system_server 不需要 AMS 发出请求。此时 AMS 自身还没有创建,必须由 Zygote 的启动流程主动拉起承载它的进程。等系统服务运行起来,AMS 才能通过 Zygote socket 请求创建应用进程。

还要注意,父子进程会从 fork 的返回点继续执行,但会走向不同分支。父进程留在 Zygote 的工作流程中,子进程则转入 SystemServer 的入口,不会继续承担 Zygote 的监听职责。对应实现见 ZygoteInit.java 和 app_main.cpp。

三、forkSystemServer:创建进程并设置身份

3.1 启动参数描述了什么

startSystemServer() 在源码中组装了一组参数,再交给 ZygoteConnection.Arguments 解析。几个关键字段如下:

参数 含义
--setuid=1000 使用系统 UID,即 AID_SYSTEM
--setgid=1000 设置主用户组
--setgroups=... 设置附加用户组
--capabilities=... 指定 permitted、effective capabilities 位掩码
--nice-name=system_server 设置进程显示名称
--runtime-args 标记后续内容按运行时参数解析
com.android.server.SystemServer Java 入口类名
这些字段分别描述 Linux 身份、进程名称和 Java 入口。

UID 1000 不等于 root,也不意味着进程拥有所有系统权限。实际访问能力还受到附加组、Linux capabilities、SELinux 策略以及服务自身权限检查的共同约束。普通第三方应用也不能仅靠在 Manifest 中声明 android.uid.system 就获得系统 UID,安装时还涉及共享 UID 的签名校验等条件。

--runtime-args 同样不是选择 fork 函数的开关。普通应用启动也会使用它;system_server 走专用路径,是因为 startSystemServer() 明确调用了 Zygote.forkSystemServer()。

3.2 父子进程如何分流

解析后的身份信息被传给 fork 方法:

java
// 代码位置 /frameworks/base/core/java/com/android/internal/os/ZygoteInit.java 第 493—549 行// startSystemServer() 中的关键片段pid = Zygote.forkSystemServer(        parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,        parsedArgs.debugFlags, null,        parsedArgs.permittedCapabilities,        parsedArgs.effectiveCapabilities); if (pid == 0) {    if (hasSecondZygote(abiList)) {        waitForSecondaryZygote(socketName);    }    handleSystemServerProcess(parsedArgs);}

成功 fork 后,父进程拿到子进程 PID,子进程拿到 0。只有子进程会进入 handleSystemServerProcess()。双 Zygote 配置下,子进程还会先等待另一个 Zygote 就绪。

Java 层的 forkSystemServer() 在调用 native 方法前后执行虚拟机 fork hooks。native 层的 nativeForkSystemServer() 则进入公共实现 ForkAndSpecializeCommon(),完成 fork,以及子进程的身份、资源限制和安全上下文等设置。

应用进程的 nativeForkAndSpecialize() 也使用这份公共实现。二者共享底层机制,但触发时机、参数和部分处理不同:

对比项 system_server 普通应用进程
触发来源 Zygote 启动参数 AMS 等发来的进程创建请求
Java fork 入口 forkSystemServer() forkAndSpecialize()
身份参数 系统预设身份与 capabilities 请求指定的应用身份等参数
常规 Java 入口 SystemServer.main() ActivityThread.main()

普通应用的顺序是“请求创建 → Zygote fork → 子进程初始化”,不会先 fork 一个应用进程,再由这个子进程请求 Zygote fork 第二次。

Zygote 记录 system_server 的 PID,在发现它退出时终止自身,让上层启动机制进行恢复。这说明 system_server 的存活与系统运行紧密相关。相关分支见 Zygote native 实现。

SystemServer 进程的启动链路:从 Zygote main 到 SystemServer.main

四、进入 SystemServer.main 之前,还要准备什么

handleSystemServerProcess() 此时已经运行在子进程中。fork 复制了进程状态,但子进程还需要清理继承的资源,并准备自己的 Java 入口。

正常启动路径主要完成四项工作:

  1. 关闭继承的 Zygote 监听 socket:子进程不负责接收进程创建请求。关闭子进程中的描述符不影响父进程继续监听。
  2. 设置文件创建掩码和进程名:umask(0077) 屏蔽新建文件或目录的组权限、其他用户权限;最终权限仍由创建时的 mode 与掩码共同决定。
  3. 准备服务类路径:读取 SYSTEMSERVERCLASSPATH,执行所需的 dex 优化,并在正常分支中构造 PathClassLoader。
  4. 进入 RuntimeInit:执行通用初始化,再转向 com.android.server.SystemServer。

类加载器解决的是“从哪里加载服务代码”,本身不会赋予系统权限。进程身份已在前面的 native fork 路径中设置。

4.1 Binder 线程池在哪里启动

RuntimeInit.zygoteInit() 的主线是:

java
// 代码位置 /frameworks/base/core/java/com/android/internal/os/RuntimeInit.java 第 269—279 行// 省略跟踪与日志设置commonInit();nativeZygoteInit();applicationInit(targetSdkVersion, argv, classLoader);

commonInit() 配置公共运行环境,例如未捕获异常处理。nativeZygoteInit() 通过原生运行时回调进入 AppRuntime::onZygoteInit(),由后者调用:

cpp
// 代码位置 /frameworks/base/cmds/app_process/app_main.cpp 第 91—95 行sp<ProcessState> proc = ProcessState::self();proc->startThreadPool();

这一步为进程建立接收 Binder 事务的线程池。后续服务发布 Binder 接口后,远程调用通常由这些 Binder 线程承接;需要在特定线程执行的工作,再由服务转交给相应 Handler 或工作线程。

Binder 线程池与稍后创建的主线程 Looper 是两套机制。主线程的 Looper.loop() 处理消息队列,并不直接充当 Binder 线程池。对应入口见 RuntimeInit.java 与 AppRuntime 实现。

4.2 为什么通过异常调用 main

applicationInit() 解析目标类及其参数,随后由 invokeStaticMain() 反射查找 public static main(String[])。在 Android 6.0 中,它没有立即调用这个方法,而是抛出携带目标方法和参数的 ZygoteInit.MethodAndArgsCaller。

异常一直回到 ZygoteInit.main(),由这里接住并执行:

java
// 代码位置 /frameworks/base/core/java/com/android/internal/os/ZygoteInit.java 第 615—616 行catch (MethodAndArgsCaller caller) {    caller.run();}

这个异常用于转移控制流,不表示启动失败。它让中间的初始化栈帧先退出,再调用目标入口。最终执行的是:

java
// 代码位置 /frameworks/base/services/java/com/android/server/SystemServer.java 第 167—169 行public static void main(String[] args) {    new SystemServer().run();}

这里的 new SystemServer() 只是创建 Java 对象。Linux 进程早已由 fork 创建,不能把这两次“创建”当成同一件事。

五、SystemServer.run:先准备环境,再启动服务

进入 run() 后,工作重心从进程创建转向系统服务初始化。省略辅助逻辑后,可以沿着下面的顺序阅读:

plain text
校正过早的系统时间、处理属性和运行时配置    ↓设置线程调度策略,准备主线程 Looper    ↓    ↓处理待执行的关机或重启请求    ↓创建系统 Context 和 SystemServiceManager    ↓startBootstrapServices()startCoreServices()startOtherServices()    ↓Looper.loop()

createSystemContext() 通过 ActivityThread.systemMain() 建立系统使用的上下文。这里调用 ActivityThread 的方法,并不意味着又创建了应用进程,也不是靠这个 Context 才取得系统 UID。

System.loadLibrary("android_servers") 加载原生库并接入 JNI 方法。加载库与发布服务是不同步骤,例如传感器服务还需要显式调用 startSensorService();不能把“库已加载”理解成“库中的服务全部注册完成”。

5.1 三个启动批次表达依赖顺序

方法 Android 6.0 中的代表服务或工作
startBootstrapServices() Installer、AMS、PowerManagerService、DisplayManagerService、PackageManagerService,以及 SensorService 的启动
startCoreServices() BatteryService、UsageStatsService、WebViewUpdateService 等
startOtherServices() AlarmManagerService、WindowManagerService、InputManagerService,以及更多系统功能

这些名称用于组织启动代码,不是把服务简单分成“重要”和“不重要”。关键在于依赖顺序:后面的初始化可能要使用前面已经创建的服务,也可能必须等某个启动阶段完成。

三个方法也不等于三个 boot phase。SystemServiceManager.startBootPhase() 会在流程中的特定位置通知已纳入管理的服务。例如等待默认显示设备的阶段已经出现在引导服务启动过程中,并非等所有其他服务创建完才开始。完整顺序见 SystemServer.java。

5.2 SystemServiceManager 管理哪些对象

SystemServiceManager.startService() 创建或接收 SystemService 实例,将其加入列表,并调用 onStart()。后续 startBootPhase() 再向列表中的服务分发 onBootPhase() 回调。

AMS 通过一个适配生命周期的内部类接入:

java
// 代码位置 /frameworks/base/services/java/com/android/server/SystemServer.java 第 329—330 行mActivityManagerService = mSystemServiceManager.startService(        ActivityManagerService.Lifecycle.class).getService();

Lifecycle 继承 SystemService,内部持有真正的 ActivityManagerService。这一层包装使 AMS 能参与统一的生命周期管理。

但并非所有服务都通过同一种入口创建。这个版本中,PackageManagerService 使用 main(...),WindowManagerService 也使用自己的静态入口,InputManagerService 则直接构造。阅读启动代码时,应分别追踪对象创建、初始化和接口发布,不能只搜索 startService()。生命周期管理实现见 SystemServiceManager.java。

六、启动服务与注册服务,是两个步骤

服务对象已经创建,并不意味着其他进程就能按名称找到它。这里需要区分三个名字相近的组件:

组件 主要职责 作用范围
SystemServiceManager 启动服务、分发启动阶段和生命周期回调 system_server 内部
LocalServices 按接口类型保存和获取本地对象 当前进程内部
ServiceManager / servicemanager 通过 Binder 注册、查询具名服务 跨进程服务发现

其中,android.os.ServiceManager 是 Java 侧的访问封装,servicemanager 是独立的原生进程。后者保存服务名与 Binder 引用的对应关系,不承载 AMS、WMS 这些服务的业务实现。

例如,窗口和输入服务在创建后分别发布 Binder 接口:

java
// 代码位置 /frameworks/base/services/java/com/android/server/SystemServer.java 第 503—504 行ServiceManager.addService(Context.WINDOW_SERVICE, wm);ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

AMS 的发布位置则在 setSystemProcess() 内部,不能仅凭 SystemServer.java 中没有直接写出 addService("activity", ...),就判断它没有注册。

有些 SystemService 会在自己的 onStart() 中调用 publishBinderService(),后者最终进入 ServiceManager.addService()。因此,“启动”和“发布”在职责上不同,在实际调用栈中却可能嵌套发生。也有只提供进程内接口的服务,无需发布 Binder 接口。

SystemServer 进程内部:SystemServiceManager 启动服务 + ServiceManager 注册 + Binder 驱动

七、ServiceManager 如何把服务交给调用方

7.1 注册:传递 Binder 引用

Java 层首次访问注册中心时,通过以下代码取得 IServiceManager 接口:

java
// 代码位置 /frameworks/base/core/java/android/os/ServiceManager.java 第 33—41 行sServiceManager = ServiceManagerNative.asInterface(        BinderInternal.getContextObject());

在 system_server 中,这里得到的是访问远程注册中心的代理。随后 addService() 将服务名、Binder 引用和 allowIsolated 标记写入 Parcel,向注册中心发起事务。

关键操作是 writeStrongBinder(service)。它传递 Binder 对象的跨进程引用,不能理解成把 Java 对象或一个可直接解引用的内存地址复制到另一进程。Binder 驱动负责处理节点与各进程中引用的映射。

allowIsolated=true 表示允许隔离进程查询该服务,但不代表绕过其他访问控制;注册中心的策略和目标服务的方法权限检查仍然生效。Java 封装与事务实现见 ServiceManager.java 和 ServiceManagerNative.java。

7.2 查询:拿到引用后直接调用目标服务

以应用进程调用 AMS 为例,Android 6.0 的内部获取路径可以概括为:

plain text
ActivityManagerNative.getDefault()    → ServiceManager.getService("activity")    → ActivityManagerNative.asInterface(binder)    → IActivityManager 接口代理

这里使用该版本的 ActivityManagerNative,避免混入其他版本的 ActivityManager.getService() 调用链。应用开发通常通过公开的 Framework API 使用系统能力,不需要直接调用隐藏的 ServiceManager 接口。对应实现见 ActivityManagerNative.java。

拿到代理以后,业务调用直接通过 Binder 发往 system_server,由 AMS 处理。servicemanager 负责查找服务,不中转后续的每一次业务调用。

SystemServer 与 ServiceManager 之间的 addService / getService 通信,经 Binder 驱动

完整过程可以拆成四步:

  1. system_server 创建 AMS,并以 activity 为名称发布它的 Binder 引用。
  2. 调用方查询 activity,取得 Binder 引用并转换为接口代理。
  3. 调用方调用代理方法,参数通过 Binder 事务进入 system_server。
  4. AMS 执行权限检查和业务逻辑,按接口约定返回结果;需要切换线程的工作由服务自行调度。

ServiceManager 中还有一个容易误读的 sCache:getService() 会先查本地缓存,但 Android 6.0 的实现不会把每次远程查询结果自动写回它。该缓存通过 initServiceCache() 初始化,具体接口的上层封装也可能另行保存代理。因此不能从“存在缓存”推导出“每个查询过的服务都被 ServiceManager 自动缓存”。

八、如何在设备上观察这条链路

源码说明启动机制,设备命令则帮助确认实际运行状态。不同 Android 版本和构建类型支持的命令选项、可见信息可能不同。

先查看进程:

bash
adb shell ps

在输出中找到 zygote、zygote64 和 system_server,比较 PID、PPID。对支持相应选项的较新设备,也可以使用:

bash
adb shell ps -A -o USER,PID,PPID,NAME

然后查看已发布的 Binder 服务和具体服务状态:

bash
adb shell service listadb shell dumpsys activityadb shell dumpsys windowadb shell dumpsys battery

service list 反映注册中心中的 Binder 服务,不等于 SystemServiceManager 的内部列表,也不会列出所有 LocalServices 对象。dumpsys 则适合观察服务当前状态,不能单凭其输出还原全部启动时序。

如需核对进程身份,可将下面的 1234 替换为实际 system_server PID;能否读取取决于设备访问限制:

bash
adb shell cat /proc/1234/status

其中 Uid、Gid 可与启动参数对照。本文没有连接设备执行这些命令,以上是供读者验证的操作步骤。

九、沿着三个边界继续读源码

阅读 SystemServer 启动代码时,始终区分这三个时间点:

  • fork 完成:system_server 进程已经存在,但系统服务尚未全部创建。
  • 服务启动:对象完成相应初始化,可能已经发布接口,但仍需等待后续启动阶段才能提供完整能力。
  • 系统就绪:依赖和启动阶段逐步满足,服务开始承担正常的系统工作。

从 Zygote 到 SystemServer.main(),解决的是进程和运行环境的问题;从 run() 到各个服务的启动入口,解决的是依赖与初始化的问题;从 addService() 到调用方持有 Binder 代理,解决的是服务发现与跨进程调用的问题。把这三段分别读清楚,再进入 AMS、PMS 或 WMS 的具体实现,就能判断每段代码处于启动过程的哪一层。


本文由 AgentWorkFlow 生成

觉得不错?给作者点个赞吧

分类:Android系统

评论 (0)

无需注册,留言经站长审核后公开。

加载评论中…

Android-Framework(四)-开机启动SystemServer进程 · Yinchen 的博客