Android 7系统异常问题排查(五)Framework层(下)—System Server崩溃

系列目录第一篇:异常机制全景图 | [第二篇:Kernel Panic 与系统重启](#第二篇:Kernel Panic 与系统重启) | [第三篇:Tombstone 机制](#第三篇:Tombstone 机制) | [第四篇:System Server Watchdog](#第四篇:System Server Watchdog) | [第五篇:System Server 崩溃](#第五篇:System Server 崩溃) | [第六篇:ANR 机制](#第六篇:ANR 机制) | [第七篇:Java 层崩溃](#第七篇:Java 层崩溃) | [第八篇:Trace 机制](#第八篇:Trace 机制) | 第九篇:日志系统 | 第十篇:实战方法论


一、System Server 崩溃 vs Watchdog 超时

在上一篇文章中我们探讨了 Watchdog 触发的"自杀"------系统卡死 → Watchdog 检测到 → 主动 killProcess。本篇分析另一种形态:System Server 因自身异常直接崩溃

维度 Watchdog 超时 直接崩溃
触发方式 Watchdog 主动自杀 未捕获异常或 Native 信号
前置症状 系统逐渐卡死 可能无明显前兆
日志特征 system_server_watchdog system_server_crashtombstone
崩溃类型 总是 Java 层自杀 Java 异常 / Native crash / OOM

二、崩溃类型分类

2.1 Java 层未捕获异常

System Server 是 Java 进程,其中任何线程抛出的未捕获异常都会导致进程崩溃:

复制代码
常见 Java 崩溃类型:
├─ NullPointerException    --- 空指针
├─ ArrayIndexOutOfBounds   --- 数组越界
├─ IllegalStateException   --- 非法状态
├─ ClassCastException      --- 类型转换错误
└─ SecurityException       --- 权限问题

2.2 Native 层崩溃

System Server 通过 JNI 调用了大量 Native 代码(libandroid_servers.solibandroid_runtime.so 等),Native 层崩溃同样会生成 tombstone。

2.3 OOM / LMK 杀掉

当系统内存极度紧张时,LMK(Low Memory Killer)可能会杀死 System Server。虽然 System Server 的 oom_score_adj 被设为较低值(-800 左右),但在极端情况下仍可能被选中。

2.4 Watchdog 触发的自杀

严格来说这也属于崩溃的一种,但我们已经在上篇文章中详细分析过。


三、崩溃处理链路源码分析

3.1 System Server 的 UncaughtExceptionHandler

System Server 进程同样使用 RuntimeInit.KillApplicationHandler 作为默认的未捕获异常处理器:

源码路径frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

java 复制代码
public class RuntimeInit {
    // ...
    private static IBinder mApplicationObject;
    private static volatile boolean mCrashing = false;

    private static final void commonInit() {
        // 设置默认的未捕获异常处理器
        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());
    }

    private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
        public void uncaughtException(Thread t, Throwable e) {
            try {
                // 防止递归崩溃
                if (mCrashing) return;
                mCrashing = true;

                // 1. 确保异常信息被记录到 logcat
                ensureLogging(t, e);

                // 2. 尝试通过 AMS 记录崩溃信息
                if (mApplicationObject == null) {
                    Slog.e(TAG, "Attempted to log uncaught exception "
                            + "with null mApplicationObject.");
                } else {
                    IActivityManager am = ActivityManagerNative.asInterface(
                            mApplicationObject);
                    am.handleApplicationCrash(mApplicationObject,
                            new ApplicationErrorReport.CrashInfo(e));
                }
            } catch (Throwable t2) {
                // 连 AMS 都联系不上时直接记录日志
                Slog.e(TAG, "Error reporting uncaught exception", t2);
            } finally {
                // 3. 终止进程
                Process.killProcess(Process.myPid());
                System.exit(10);
            }
        }
    }
}

关键设计ensureLogging() 确保异常堆栈被写入 logcat,即使 AMS 调用失败也能从日志中看到崩溃信息。mCrashing 标志防止递归崩溃(handler 自身异常时不会再次调用)。

3.2 System Server 与普通 App 崩溃处理的区别

特性 普通 App System Server
FC 对话框 弹出"已停止运行" 不弹框(系统进程无 UI)
进程影响 仅该 App 被杀 整个系统服务重启
崩溃后 用户可重新打开 App Zygote 自动重新 fork
crashCount 限制 连续崩溃后不再弹框 N/A

3.3 AMS.handleApplicationCrash() 的处理

当 System Server 自身调用 AMS.handleApplicationCrash() 时,实际上 System Server 自己就是 AMS 的宿主------这相当于"自己通知自己即将崩溃"。

源码路径frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

java 复制代码
public class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
    // ...

    @Override
    public void handleApplicationCrash(IBinder app,
            ApplicationErrorReport.CrashInfo crashInfo) {
        ProcessRecord r = findAppProcess(app, "Crash");
        final String processName = app == null ? "system_server"
                : r == null ? "unknown" : r.processName;

        // 1. 添加 DropBox 条目
        addErrorToDropBox(
            "system_server".equals(processName) ? "system_server_crash"
                : "crash",
            r, processName, null, null, null, null, null, crashInfo);

        // 2. 崩溃日志输出到 logcat
        Slog.e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + processName);

        // 3. 如果进程还存在,尝试收集线程堆栈
        if (app != null && r != null) {
            // dump stack traces
            // ...
        }
    }
}

关键设计 :System Server 崩溃时 DropBox tag 是 system_server_crash,而普通 App 是 data_app_crash------这决定了后续日志分析时的过滤关键字。


四、Zygote 重启机制

4.1 Zygote 如何检测 System Server 死亡?

Zygote 进程在 fork System Server 之后,会进入 runSelectLoop() 等待子进程退出:

源码路径frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

java 复制代码
public class ZygoteInit {
    // ...
    public static void main(String argv[]) {
        try {
            // ... 初始化 ...
            if (startSystemServer) {
                pid = Zygote.forkSystemServer(...);
                if (pid == 0) {
                    // 子进程:启动 System Server
                    handleSystemServerProcess(parsedArgs);
                    return;
                }
            }

            // 父进程(Zygote):进入主循环
            runSelectLoop(abiList);
            // ...
        } catch (MethodAndArgsCaller caller) {
            caller.run();
        }
    }

    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
        // ...
        while (true) {
            // 使用 select/poll 等待子进程退出或新连接
            // 当 system_server 退出时,Zygote 会检测到并退出
        }
    }
}

关键设计 :Zygote 通过 runSelectLoop() 中的 select() 系统调用监听子进程状态。当 system_server 退出时,Zygote 会检测到 SIGCHLD 信号并退出自身进程。

4.2 init 进程的编排作用

源码路径system/core/rootdir/init.zygote32_64.rc

ini 复制代码
service zygote /system/bin/app_process32 -Xzygote /system/bin \
        --zygote --start-system-server --socket-name=zygote
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd

service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin \
        --zygote --socket-name=zygote_secondary
    class main
    onrestart restart zygote

注意onrestart 配置的是重启 Zygote 依赖的 native 服务(audioserver、cameraserver 等),而非 system_server。当 Zygote 退出时,init 会根据 onrestart 配置重新拉起 Zygote,新 Zygote 启动时会通过 --start-system-server 参数自动重新 fork system_server。

4.3 完整重启链路

复制代码
System Server 崩溃
    ↓
Zygote 检测到子进程退出(通过 runSelectLoop 中的 select)
    ↓
Zygote 自身退出
    ↓
init 进程检测到 Zygote 退出(SIGCHLD)
    ↓
init 根据 init.zygote*.rc 中 onrestart 配置:
    ├─ restart audioserver/cameraserver/media/netd
    └─ 重新拉起 Zygote(因为 service 配置了 class main)
    ↓
新 Zygote 启动 → fork 出新的 System Server
    ↓
System Server 重新初始化所有服务(AMS/WMS/PMS...)
    ↓
用户感知:屏幕短暂黑屏/卡顿后恢复("热重启")

五、Boot Loop 机制保护

5.1 问题场景

如果 System Server 在启动阶段就崩溃,Zygote 会不断重启,形成 Boot Loop(反复重启循环),设备永远无法进入正常使用状态。

5.2 RescueParty(AOSP 7 引入)

AOSP 7 引入了 RescueParty 机制来应对 Boot Loop:

源码路径frameworks/base/services/core/java/com/android/server/RescueParty.java

java 复制代码
public class RescueParty {
    // ...
    public static void noteBoot(Context context) {
        // 系统启动成功后调用
        // 重置重启计数
    }

    public static void noteSystemServerRestart(Context context) {
        // System Server 每次重启时调用
        // 如果在短时间内重启次数过多 → 进入救援模式
        if (getRestartCount() > THRESHOLD) {
            executeRescueLevel(context, nextLevel());
        }
    }

    private static void executeRescueLevel(Context context, int level) {
        switch (level) {
            case LEVEL_RESET_SETTINGS:
                // 1. 重置系统设置到出厂值
                resetGlobalSettings(context);
                break;
            case LEVEL_RESET_TRUSTED_DEFAULTS:
                // 2. 恢复可信任的默认配置
                resetTrustedDefaults(context);
                break;
            case LEVEL_FACTORY_RESET:
                // 3. 恢复出厂设置
                RecoverySystem.rebootWipeUserData(context, ...);
                break;
        }
    }
}

关键设计:RescueParty 通过持久化存储记录 System Server 重启次数。如果短时间内重启超过阈值,会逐级执行恢复策略:重置设置 → 恢复默认 → 恢复出厂。

Rescue Level 递进

复制代码
频繁重启 Level 1 → 重置系统设置
仍然重启 Level 2 → 恢复可信任默认值
仍然重启 Level 3 → 提示用户恢复出厂设置

六、日志产物分析

6.1 logcat 中的关键日志

复制代码
// Java 层崩溃
AndroidRuntime: *** FATAL EXCEPTION IN SYSTEM PROCESS: main
AndroidRuntime: java.lang.NullPointerException: ...
AndroidRuntime:     at com.android.server.am.ActivityManagerService.xxx(AMS.java:1234)

// Native 层崩溃
libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 1234 (system_server)

// Zygote 重启
Zygote: Process 1234 exited due to signal 11
Zygote: Exit zygote because system server (1234) has terminated

6.2 DropBox 条目

bash 复制代码
dumpsys dropbox system_server_crash --print

输出包含完整的 Java 异常堆栈:

复制代码
Tag: system_server_crash
    Process: system_server
    Flags: 0x28...
    Package: android
    Subject: system_server
    ...
    java.lang.NullPointerException
        at com.android.server.am.ActivityManagerService.xxx(AMS.java:1234)
        at ...

6.3 tombstone(Native Crash 时)

如果 System Server 是 Native 层崩溃,/data/tombstones/ 中也会有墓碑文件,按第三篇的方法还原即可。


七、定位方法论

区分 Watchdog 自杀 vs 直接崩溃

判断依据 Watchdog 自杀 直接崩溃
logcat 关键字 WATCHDOG KILLING SYSTEM PROCESS FATAL EXCEPTION IN SYSTEM PROCESS
DropBox tag system_server_watchdog system_server_crash
堆栈特征 线程多处于 Blocked/Waiting 明确抛出 java.lang.XXX 异常
tombstone 一般没有 Native crash 时有

定位步骤

复制代码
1. 确认崩溃类型
   → logcat 搜索 "FATAL EXCEPTION IN SYSTEM PROCESS"
   → 或 "Fatal signal" (Native crash)

2. 提取异常信息
   → dumpsys dropbox system_server_crash --print
   → 或 /data/tombstones/tombstone_XX (Native crash)

3. 分析异常堆栈
   → Java crash: 直接看异常类名 + 堆栈行号
   → Native crash: ndk-stack 还原

4. 回溯崩溃前日志
   → logcat -b all -d | grep -B 50 "FATAL EXCEPTION"
   → 寻找崩溃前的异常操作或错误日志

5. 确定根因
   → 代码逻辑错误 → 修改代码
   → 资源问题(OOM) → 优化内存
   → 驱动问题(Native crash) → 排查驱动

八、总结

  1. System Server 崩溃有四种形态:Java 异常、Native crash、OOM/LMK、Watchdog 自杀。

  2. KillApplicationHandler 是所有 Java 崩溃的统一入口,System Server 与普通 App 共用但处理不同(不弹 FC 框)。

  3. Zygote runSelectLoop + init onrestart = 自动恢复机制:System Server 死亡后 Zygote 退出 → init 重新拉起 → 系统热重启。

  4. RescueParty 防止 Boot Loop:多次启动失败后逐级执行恢复策略,最终可能触发恢复出厂设置。

  5. 日志产物区分 Watchdog vs Crashsystem_server_watchdog vs system_server_crash

下一篇将进入应用层异常------ANR 机制全解。


本文基于 AOSP 7(Android Nougat)源码编写

相关推荐
AFinalStone2 小时前
Android 7系统异常问题排查(八)系统追踪—Trace机制与性能诊断
android·系统异常
极客猴子6 小时前
Android录音转写频繁卡顿?多款APP长时间会议场景稳定性实测
android·人工智能·智能手机·飞书
Kapaseker6 小时前
Boolean 变量到底该怎么命名?
android·kotlin
AFinalStone6 小时前
Android 7系统异常问题排查(六)应用异常(上)—ANR机制全解
android·系统异常
雾屿_Mistisle6 小时前
文件包含漏洞(File Inclusion)
android
小孔龙7 小时前
Android 图形系统全景
android·计算机图形学
2501_915921437 小时前
详细解析,iOS 应用上架 App Store 的完整流程与指南
android·ios·小程序·https·uni-app·iphone·webview
qq_425516187 小时前
录音转文字工具免费下载:免费额度与功能限制对比
android·人工智能·智能手机·powerpoint
plainGeekDev7 小时前
Robolectric → 分层测试:测试策略重构
android·java·kotlin