崩溃恢复链
本文面向已经读过 SystemServer.run、ZygoteInit.main、SIGCHLD 进程死亡处理 和 首次启动与普通启动 的读者。前文分别介绍了启动入口、进程创建和启动场景;本文只回答一个更窄的问题:system_server 已经运行后发生 Java 异常、Watchdog 超时或 native crash,哪个对象先观察到退出,谁决定杀掉谁,谁负责再次启动,以及反复失败时何时停止重启。
这里的“恢复”不是一个统一的 restart API。Java 未捕获异常负责留下 crash 日志并终止当前进程;Watchdog 负责在 system_server 卡住时收集线程现场并终止进程;native Zygote 负责收割 system_server 子进程并让自己退出;init 负责依据 service 状态重新拉起 Zygote。每一层都有自己的 owner、状态和生效时机。把它们混为“AMS 收到崩溃后重启 system_server”,会漏掉真正的父子进程关系和 crash-loop 保护。
1. 恢复边界
system_server 是由 primary Zygote fork 出来的子进程。它退出时,直接父进程不是 init,而是 Zygote;但是 Zygote 不能继续作为“正常父进程”运行,因为它的关键子进程已经消失。因此恢复链是:
图中的两个动作不要颠倒:system_server 退出只产生 SIGCHLD;是 Zygote 的 native handler 识别到保存的 system-server PID 后才主动杀掉 Zygote。init 看到的是 Zygote service 退出,而不是直接看到一次“重启 SystemServer”命令。
| 层 | owner | 观察对象 | 关键输出 | 是否负责再次启动 |
|---|---|---|---|---|
| Java runtime | RuntimeInit | 未捕获异常 | crash log、sys.system_server.crash_java | 否 |
| system_server watchdog | Watchdog | Handler/monitor 是否完成 | thread dump、DropBox、watchdog atom | 否,终止自己 |
| Zygote native | SigChldHandler | 子进程 waitpid() 状态 | Process ... exited、SIGKILL Zygote | 否,触发父级退出 |
| init service manager | Service::Reap | Zygote service 退出 | onrestart、crash count、critical 处置 | 是 |
2. Java异常
Java 层的入口在 RuntimeInit.commonInit() 注册。这里的 pre-handler 只负责日志,default handler 才负责停止进程;它并不调用 init,也不直接创建新的 Zygote。
源码文件:frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
相关函数:RuntimeInit.commonInit
protected static final void commonInit() {
if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
// 本文注:pre-handler 与 default handler 分工,前者不能替代后者。
LoggingHandler loggingHandler = new LoggingHandler();
RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
Thread.setDefaultUncaughtExceptionHandler(
new KillApplicationHandler(loggingHandler));
// ... 设置时区、java.util.logging、HTTP User-Agent 和 socket tagging。
}RuntimeHooks.setUncaughtExceptionPreHandler() 对所有线程生效,应用可以替换 default handler,但不能替换这个 pre-handler。SystemServer 通过 Zygote 进入同一套 runtime 初始化,因此 system_server 线程的未捕获异常也先经过 LoggingHandler。
相关类:RuntimeInit.LoggingHandler
private static class LoggingHandler
implements Thread.UncaughtExceptionHandler {
public volatile boolean mTriggered = false;
@Override
public void uncaughtException(Thread t, Throwable e) {
mTriggered = true;
// 本文注:crash handler 重入时不重复写日志。
if (mCrashing) return;
// mApplicationObject == null 且 UID 为 SYSTEM 才是 system_server 特殊日志。
if (mApplicationObject == null
&& Process.SYSTEM_UID == Process.myUid()) {
Clog_e(TAG,
"*** FATAL EXCEPTION IN SYSTEM PROCESS: "
+ t.getName(), e);
mCrashCount = SystemProperties.getInt(
SYSPROP_CRASH_COUNT, 0) + 1;
SystemProperties.set(
SYSPROP_CRASH_COUNT,
String.valueOf(mCrashCount));
} else {
logUncaught(t.getName(),
ActivityThread.currentProcessName(),
Process.myPid(), e);
}
}
}这里的 owner 是 runtime 进程内的静态字段。mApplicationObject == null 是区分 system_server 特殊日志格式的条件,不能简化成“UID 为 1000 就一定是 system_server”,因为 system UID 的其他 Java 程序也可能满足部分条件。sys.system_server.crash_java 只在这条日志路径递增,保存的是 Java crash 次数,不是所有 crash 的总数。
相关类:RuntimeInit.KillApplicationHandler
private static class KillApplicationHandler
implements Thread.UncaughtExceptionHandler {
private final LoggingHandler mLoggingHandler;
@Override
public void uncaughtException(Thread t, Throwable e) {
try {
ensureLogging(t, e);
// 本文注:先置位再执行报告,防止报告路径再次触发无限递归。
if (mCrashing) return;
mCrashing = true;
if (ActivityThread.currentActivityThread() != null) {
// profiling buffer 在进程被杀前尽量刷新。
ActivityThread.currentActivityThread()
.stopProfiling();
}
// system_server 的 ActivityManager Binder 可能已不可用。
ActivityManager.getService()
.handleApplicationCrash(
mApplicationObject,
new ApplicationErrorReport
.ParcelableCrashInfo(e));
} catch (Throwable t2) {
if (!(t2 instanceof DeadObjectException)) {
try {
Clog_e(TAG,
"Couldn't report crash. Here's the crash:", e);
Clog_e(TAG,
"Error reporting crash. Here's the error:", t2);
} catch (Throwable ignored) {
// 日志系统也失败时不能再依赖报告链。
}
}
} finally {
// 无论 AMS 报告是否成功,都必须让 system_server 消失。
Process.killProcess(Process.myPid());
System.exit(10);
}
}
}这段代码有两个提交顺序。第一,ensureLogging() 保障日志先于报告;第二,finally 无条件执行进程终止。handleApplicationCrash() 不是恢复入口,它最多显示/记录 crash;如果 AMS 本身已死,DeadObjectException 会被忽略,不能阻止 killProcess()。进程退出后,恢复责任转移到 Zygote 和 init。
3. Watchdog
Java 异常是“线程主动结束”,Watchdog 是“线程没有在期限内完成”。Watchdog 线程本身位于 system_server,但它不直接修复阻塞的服务;它在收集现场后决定是否杀死 system_server。
源码文件:frameworks/base/services/core/java/com/android/server/Watchdog.java
相关函数:Watchdog.run
private void run() {
boolean preWatchdogTriggered = false;
Throttler preWatchdogThrottler = new Throttler(
SystemClock.uptimeClock(),
PRE_WATCHDOG_COOL_OFF_MILLIS);
while (true) {
// 本文注:每轮使用同一个 timeout 快照,避免运行中配置变化造成混合判断。
final long watchdogTimeoutMillis =
mWatchdogTimeoutMillis;
final long checkIntervalMillis =
watchdogTimeoutMillis
/ PRE_WATCHDOG_TIMEOUT_RATIO;
List<HandlerChecker> blockedCheckers =
Collections.emptyList();
boolean allowRestart = true;
synchronized (mLock) {
long timeout = checkIntervalMillis;
for (int i = 0;
i < mHandlerCheckers.size(); i++) {
HandlerCheckerAndTimeout hc =
mHandlerCheckers.get(i);
hc.checker().scheduleCheckLocked(
hc.customTimeoutMillis().orElse(
watchdogTimeoutMillis
* Build.HW_TIMEOUT_MULTIPLIER));
}
// uptime 不计算 deep sleep,避免休眠期间误杀。
long start = SystemClock.uptimeMillis();
while (timeout > 0) {
try {
mLock.wait(timeout);
} catch (InterruptedException e) {
Log.wtf(TAG, e);
}
timeout = checkIntervalMillis
- (SystemClock.uptimeMillis() - start);
}
final int waitState =
evaluateCheckerCompletionLocked();
if (waitState == COMPLETED
|| waitState == WAITING) {
if (waitState == COMPLETED) {
preWatchdogTriggered = false;
}
continue;
}
blockedCheckers = getCheckersWithStateLocked(
waitState == OVERDUE
? OVERDUE
: WAITED_UNTIL_PRE_WATCHDOG);
allowRestart = mAllowRestart;
}
// ... 锁外记录 critical event、DropBox 和线程 dump。
}
}检查器状态由 mLock 保护,实际的 Handler/monitor 回调在各自线程执行。WAITED_UNTIL_PRE_WATCHDOG 只触发预警和现场收集,循环继续等待完整 timeout;只有 OVERDUE 才进入终止决策。把预警当成已经重启,会把两次 dump 和一次 kill 的时机看错。
相关函数:Watchdog.run 的终止分支
// 本文注:debugger 或测试 controller 可以阻止 kill,生产超时不一定必然退出。
if (Debug.isDebuggerConnected()) {
debuggerWasConnected = 2;
}
if (debuggerWasConnected >= 2) {
Slog.w(TAG,
"Debugger connected: Watchdog is *not* killing the system process");
} else if (!allowRestart) {
Slog.w(TAG,
"Restart not allowed: Watchdog is *not* killing the system process");
} else {
Slog.w(TAG,
"*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);
WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);
Slog.w(TAG, "*** GOODBYE!");
if (!Build.IS_USER && isCrashLoopFound()
&& !WatchdogProperties
.should_ignore_fatal_count()
.orElse(false)) {
breakCrashLoop();
}
Process.killProcess(Process.myPid());
System.exit(10);
}Watchdog 的 owner 仍然是 system_server 内的 watchdog 线程;allowRestart 来自 mAllowRestart,debugger 连接和 activity controller 都可能把“发现阻塞”与“终止进程”分开。真正 kill 前会写 watchdog 事件并收集诊断,kill 之后不会在本进程内执行恢复逻辑。
4. Zygote接管
SystemServer 的 Java 进程退出后,primary Zygote 通过 native SIGCHLD handler 收割子进程。这个 handler 必须使用 waitpid(),否则退出的 system_server 会变成 zombie,Zygote 也无法准确知道哪个 PID 已经结束。
源码文件:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
相关函数:SigChldHandler
static void SigChldHandler(int /*signal_number*/,
siginfo_t* info,
void* /*ucontext*/) {
pid_t pid;
int status;
int64_t usaps_removed = 0;
int saved_errno = errno;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
// 本文注:先把退出状态发送给 system_server 的 AppExitInfo 记录器。
sendSigChildStatus(pid, info->si_uid, status);
if (WIFEXITED(status)) {
async_safe_format_log(
ANDROID_LOG_INFO, LOG_TAG,
"Process %d exited cleanly (%d)",
pid, WEXITSTATUS(status));
if (RemoveUsapTableEntry(pid)) {
++usaps_removed;
}
} else if (WIFSIGNALED(status)) {
async_safe_format_log(
ANDROID_LOG_INFO, LOG_TAG,
"Process %d exited due to signal %d (%s)%s",
pid, WTERMSIG(status),
strsignal(WTERMSIG(status)),
WCOREDUMP(status) ? "; core dumped" : "");
if (WTERMSIG(status) != SIGTERM
&& RemoveUsapTableEntry(pid)) {
++usaps_removed;
}
}
// 本文注:gSystemServerPid 是 Zygote native owner 保存的目标 PID。
if (pid == gSystemServerPid) {
async_safe_format_log(
ANDROID_LOG_ERROR, LOG_TAG,
"Exit zygote because system server (pid %d) has terminated",
pid);
kill(getpid(), SIGKILL);
}
}
// ECHILD 对没有子进程的 secondary zygote 是正常情况。
if (pid < 0 && errno != ECHILD) {
async_safe_format_log(
ANDROID_LOG_WARN, LOG_TAG,
"Zygote SIGCHLD error in waitpid: %s",
strerror(errno));
}
// ... 如果有 USAP 被收割,通知 ZygoteServer 更新资源池。
errno = saved_errno;
}这里的 sendSigChildStatus() 是给 system_server 内部的退出信息通道;当 system_server 自己已经死掉时,消息可能无法被消费,但这不影响后面的 PID 比较和 kill(getpid(), SIGKILL)。WNOHANG 让 handler 一次收割当前已经结束的多个子进程;保存并恢复 errno 是 signal handler 的并发约束。
Zygote 发送的三个整数字段在 system_server 由 ProcessList 消费。它使用 native parser 先校验消息类型和长度,再将 PID、UID 和 wait status 传给 AppExitInfoTracker。
源码文件:frameworks/base/services/core/java/com/android/server/am/ProcessList.java
相关函数:ProcessList.handleZygoteMessages
private int handleZygoteMessages(
FileDescriptor fd, int events) {
final int eventFd = fd.getInt$();
if ((events & EVENT_INPUT) != 0) {
try {
int len = Os.read(fd,
mZygoteUnsolicitedMessage, 0,
mZygoteUnsolicitedMessage.length);
if (len > 0
&& mZygoteSigChldMessage.length
== Zygote.nativeParseSigChld(
mZygoteUnsolicitedMessage,
len,
mZygoteSigChldMessage)) {
// 本文注:只有消息格式正确才更新进程退出信息。
mAppExitInfoTracker.handleZygoteSigChld(
mZygoteSigChldMessage[0] /* pid */,
mZygoteSigChldMessage[1] /* uid */,
mZygoteSigChldMessage[2] /* status */);
}
} catch (Exception e) {
Slog.w(TAG,
"Exception in reading unsolicited zygote message: "
+ e);
}
}
return EVENT_INPUT;
}ProcessList 只记录子进程的退出信息,不参与决定 Zygote 是否自杀。因此它的消费时机可能晚于 SIGCHLD,也可能因 system_server 已经退出而无法保存;这不会改变 Zygote 的恢复决策。
相关函数:forkSystemServer
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
jint runtime_flags, jlong permitted_capabilities,
jlong effective_capabilities) {
// ... 准备 fd、rlimit、mount 和 capability 参数。
SetSignalHandlers();
BlockSignal(SIGCHLD, fail_fn);
pid_t pid = zygote::ForkCommon(
env, true, fds_to_close, fds_to_ignore, true);
if (pid == 0) {
SpecializeCommon(
env, uid, gid, gids, runtime_flags,
rlimits, permitted_capabilities,
effective_capabilities, 0,
MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr,
true, false, nullptr, nullptr,
false, nullptr, nullptr, false, false, false);
} else if (pid > 0) {
ALOGI("System server process %d has been created", pid);
gSystemServerPid = pid;
// 本文注:发布 PID 后立即复查极窄的 fork/记录竞态。
int status;
if (waitpid(pid, &status, WNOHANG) == pid) {
ALOGE("System server process %d has died. Restarting Zygote!", pid);
RuntimeAbort(env, __LINE__,
"System server process has died. Restarting Zygote!");
}
}
return pid;
}gSystemServerPid 的 owner 是每个 Zygote 进程的 native 全局状态,值在 parent 分支写入;child 分支继续执行 SystemServer specialization。SetSignalHandlers() 之所以在 fork 前设置,是为了让 Zygote 能收割后续子进程;fork 窗口又临时 block SIGCHLD,避免 fd 清理阶段被 handler 插入。发布 PID 后的 waitpid(WNOHANG) 是补偿检查,用来覆盖“子进程已死但 handler 尚未观察到”的窗口。
5. init重启
init 只认识 service。primary Zygote 的 rc 定义包含 onrestart 和 critical;它没有一条“SystemServer 崩溃后直接启动 SystemServer”的规则。因为 SystemServer 是 Zygote 的子进程,init 的重启对象是 zygote service。
源码文件:system/core/rootdir/init.zygote64.rc
相关配置:service zygote
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart write /sys/power/wake_lock zygote_kwl
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart --only-if-running media.tuner
onrestart restart netd
onrestart restart wificond
task_profiles ProcessCapacityHigh MaxPerformance
critical window=${zygote.critical_window.minute:-off} target=zygote-fatalonrestart 命令在 service 被标记为 restarting 后执行,消费方是 init 的 Action 执行器;它们会恢复 FUSE、电源唤醒和一组依赖的 native 服务。critical 是独立的 crash-loop 策略,不表示每次退出都立即重启设备。
secondary Zygote 的定义还包含 onrestart restart zygote。因此 32 位 secondary Zygote 异常退出时,init 会请求 primary Zygote 重启;这条规则和 system_server 的 primary 子进程退出路径不能混为一谈。
相关配置:service zygote_secondary
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
class main
priority -20
user root
group root readproc reserved_disk
socket zygote_secondary stream 660 root system
socket usap_pool_secondary stream 660 root system
onrestart restart zygote
task_profiles ProcessCapacityHigh MaxPerformanceprimary Zygote 重新启动后,app_process64 再次进入 ZygoteInit.main(),因为 rc 命令行仍带 --start-system-server,它会重新走 forkSystemServer()。新的 SystemServer 构造函数会把 sys.system_server.start_count 加一,因此可用 启动场景判定 的计数区分完整物理 boot 与 runtime restart。
6. 重启状态机
init 的 Service::Reap() 同时处理资源清理、失败回调、crash-loop 计数和状态转换。关键状态不是“进程死了”这一瞬间,而是 pid_ = 0、SVC_RESTARTING 和下一次 Start() 之间的顺序。
源码文件:system/core/init/service.cpp
相关函数:Service::Reap
void Service::Reap(const siginfo_t& siginfo) {
// 本文注:非 oneshot 或正在重启的 service 先清理整个进程组。
if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
KillProcessGroup(SIGKILL);
}
for (const auto& socket : sockets_) {
if (socket.persist) continue;
auto path = ANDROID_SOCKET_DIR "/" + socket.name;
unlink(path.c_str());
}
for (const auto& f : reap_callbacks_) {
f(siginfo);
}
if ((siginfo.si_code != CLD_EXITED
|| siginfo.si_status != 0)
&& on_failure_reboot_target_) {
trigger_shutdown(*on_failure_reboot_target_);
}
if (flags_ & SVC_TEMPORARY) return;
pid_ = 0;
flags_ &= (~SVC_RUNNING);
start_order_ = 0;
was_last_exit_ok_ = siginfo.si_code == CLD_EXITED
&& siginfo.si_status == 0;
if (flags_ & (SVC_DISABLED | SVC_RESET)) {
NotifyStateChange("stopped");
return;
}
flags_ &= (~SVC_RESTART);
flags_ |= SVC_RESTARTING;
onrestart_.ExecuteAllCommands();
NotifyStateChange("restarting");
}清理顺序有实际含义:先杀进程组,避免 service 派生的工作进程遗留;再删除非持久 socket;然后调用 reap callback;最后清掉 PID 并进入 SVC_RESTARTING。onrestart 在状态已经变成 restarting 后执行,因此命令看到的是“旧实例已被收割、等待新实例”的状态。
下一次启动由 init 主循环中的 HandleProcessActions() 触发。
相关函数:HandleProcessActions
static std::optional<boot_clock::time_point>
HandleProcessActions() {
std::optional<boot_clock::time_point>
next_process_action_time;
for (const auto& s : ServiceList::GetInstance()) {
if ((s->flags() & SVC_RUNNING)
&& s->timeout_period()) {
// ... 处理 timeout service。
}
if (!(s->flags() & SVC_RESTARTING)) continue;
auto restart_time = s->time_started()
+ s->restart_period();
if (boot_clock::now() > restart_time) {
if (auto result = s->Start(); !result.ok()) {
LOG(ERROR) << "Could not restart process '"
<< s->name() << "': "
<< result.error();
}
} else {
if (!next_process_action_time
|| restart_time
< *next_process_action_time) {
next_process_action_time = restart_time;
}
}
}
return next_process_action_time;
}SVC_RESTARTING 是等待状态,Start() 才是新的进程创建动作;若 restart_period 尚未到期,init 不忙等,而是把最近的时间点交还主循环。启动失败不会把旧 PID 恢复为 running,下一轮仍依据 service 状态处理。
7. CrashLoop保护
critical 的含义是“连续失败达到阈值后升级处置”,不是“每次失败都触发 bootloader”。在 Android 17 的 init 实现中,关键 service 在 boot 未完成前或时间窗口内反复非正常退出,超过 4 次才进入 fatal 分支;并且 24 小时内已经执行过一次 fatal reboot 时会节流。
源码文件:system/core/init/service.cpp
相关函数:Service::Reap 的 crash-loop 分支
// 本文注:只有 critical 或 updatable service 的非正常退出进入计数。
if (((flags_ & SVC_CRITICAL) || is_process_updatable)
&& !(flags_ & SVC_RESTART)
&& !was_last_exit_ok_) {
bool boot_completed =
GetBoolProperty("sys.boot_completed", false);
if (now < time_crashed_ + fatal_crash_window_
|| !boot_completed) {
if (++crash_count_ > 4) {
if (flags_ & SVC_CRITICAL) {
if (!GetBoolProperty(
"init.svc_debug.no_fatal."
+ name_, false)) {
uint64_t epoch_time =
std::chrono::duration_cast<
std::chrono::seconds>(
std::chrono::system_clock::now()
.time_since_epoch())
.count();
if (epoch_time
- GetIntProperty(
"persist.init.svc.last_fatal_reboot_epoch",
0)
> 24 * 60 * 60) {
SetProperty(
"persist.init.svc.last_fatal_reboot_epoch",
std::to_string(epoch_time));
SetFatalRebootTarget(
fatal_reboot_target_);
LOG(FATAL)
<< "critical process '"
<< name_
<< "' exited 4 times";
}
}
} else {
// updatable service 通知 update_verifier/apexd,而非立即 fatal reboot。
SetProperty(
"sys.init.updatable_crashing_process_name",
name_);
SetProperty(
"sys.init.updatable_crashing", "1");
}
}
} else {
time_crashed_ = now;
crash_count_ = 1;
}
}这里的计数 owner 是 init 内存中的 Service 对象,time_crashed_ 和 crash_count_ 随 init 生命周期存在;persist.init.svc.last_fatal_reboot_epoch 只用于跨重启节流。init.svc_debug.no_fatal_<service> 可关闭 fatal 处置,开发和测试环境因此可能看到 crash loop 继续重启而不进入 bootloader。上面的 updatable 分支也说明 APEX 可更新 service 的失败后果和 critical 不同。
8. 诊断输出
一次完整诊断要把“根因现场”和“恢复动作”分开采集。Java crash、Watchdog 和 native crash 的输出位置不同,不能只看 logcat | grep FATAL。
8.1 Java与Watchdog
RuntimeInit.LoggingHandler 写入 crash log,并在 system UID 的 system_server 路径递增 sys.system_server.crash_java;Watchdog 另写 WATCHDOG EventLog、SYSTEM_SERVER_WATCHDOG_OCCURRED atom,并把线程 dump 放入 DropBox。
# 输入:当前设备的 system_server 属性。输出:Java crash 累计值与本次 runtime restart 次数。
adb shell getprop sys.system_server.crash_java
adb shell getprop sys.system_server.start_count
# 输入:system_server dump 服务。输出:本次进程的 Runtime restart、start count 和时钟。
adb shell dumpsys system_server_dumper --name SystemServer
# 输入:system 与 main buffer。输出:Java fatal、Watchdog kill 和 Zygote 收割日志。
adb logcat -b system -b main -v threadtime -d \
| rg 'FATAL EXCEPTION IN SYSTEM PROCESS|WATCHDOG KILLING SYSTEM PROCESS|Exit zygote because system server|Process [0-9]+ exited'
# 输入:DropBox 服务。输出:watchdog、system_server crash 现场条目。
adb shell dumpsys dropbox --print system_server_native_crash
adb shell dumpsys dropbox --print watchdog这些命令的输入分别是 system properties、SystemServerDumper Binder service、log buffers 和 DropBoxManager;输出只能证明对应记录是否存在,不能单独证明 init 已经完成重启。要确认恢复动作,还需要读取 init service 状态和后续 SYSTEM_SERVER_START。
8.2 Native与init
native crash 的 tombstone 由 debuggerd 生成,BootReceiver 在启动后的后台线程把包含 >>> system_server <<< 的 tombstone 额外复制为 system_server_native_crash。init 的服务状态则通过 init.svc.zygote 和 crash-loop 属性观察。
源码文件:frameworks/base/services/core/java/com/android/server/BootReceiver.java
相关函数:BootReceiver.addFileWithFootersToDropBox
String fileContents = FileUtils.readTextFile(
file, maxSize, TAG_TRUNCATED);
String text = headers + fileContents + footers;
// 本文注:只有 tombstone 且内容标记为 system_server 才使用专用 tag。
if (tag.equals(TAG_TOMBSTONE)
&& fileContents.contains(">>> system_server <<<")) {
addTextToDropBox(db,
"system_server_native_crash",
text, filename, maxSize);
}
if (tag.equals(TAG_TOMBSTONE)) {
FrameworkStatsLog.write(
FrameworkStatsLog.TOMB_STONE_OCCURRED);
}
addTextToDropBox(db, tag, text, filename, maxSize);BootReceiver 是诊断输出的后处理者,不是 native crash 的终止者。它只是根据 tombstone 文本内容增加一个精确 tag;如果 DropBox tag 未启用、文件时间戳已记录或文件不存在,该副作用可以缺失,但不影响 debuggerd 和 init 的进程处理。
# 输入:init property service。输出:zygote 当前状态及 crash-loop 节流时间。
adb shell getprop init.svc.zygote
adb shell getprop persist.init.svc.last_fatal_reboot_epoch
# 输入:kernel/debuggerd tombstone 目录。输出:最近 system_server native 崩溃的线程和 signal。
adb shell ls -lt /data/tombstones | head
adb shell grep -R -n '>>> system_server <<<' /data/tombstones
# 输入:启动时间线。输出:旧 zygote 退出、重启和新 system_server start 的相邻事件。
adb logcat -b all -v threadtime -d \
| rg 'system_server.*terminated|Exit zygote|init.*zygote|SYSTEM_SERVER_START|BOOT_PROGRESS_SYSTEM_RUN'如果 sys.system_server.crash_java 增长但没有 tombstone,优先检查 Java 异常路径;如果有 tombstone 但没有 FATAL EXCEPTION IN SYSTEM PROCESS,说明根因可能在 native;如果 Zygote 日志出现退出但 init.svc.zygote 长时间不是 running,应转向 init rc、SELinux 或 executable 启动失败,而不是继续分析 AMS。
9. 调用时序
下图把三个进程边界放在一条时间轴上。消息 SIGCHLD 是内核到 Zygote 的信号,onrestart 是 init 内部 Action,不是 Binder 调用。
system_server 的 Java 报告动作和 Zygote 的退出动作之间没有同步等待关系:Java handler 只等待 handleApplicationCrash() 的 Binder 调用返回或失败,然后立即终止;Zygote 收到的只是最终进程状态。init 的 Start() 发生在之后的主循环时间点,因此“crash 日志出现”和“新 SystemServer 已经开始”不是同一个时间戳。
10. 失败矩阵
| 现象 | 第一观察者 | 关键状态 | 可能的下一步 |
|---|---|---|---|
Java FATAL EXCEPTION IN SYSTEM PROCESS | RuntimeInit.LoggingHandler | sys.system_server.crash_java 增加 | 查原始 Java stack,再看 Zygote 是否退出 |
WATCHDOG KILLING SYSTEM PROCESS | Watchdog | checker OVERDUE、allowRestart | 查 blocked checker、thread dump、debugger/controller 条件 |
tombstone 含 >>> system_server <<< | debuggerd/BootReceiver | signal、native backtrace、DropBox tag | 查 native 库和对应 service 调用 |
Zygote 日志 Exit zygote because system server... | Zygote SigChldHandler | pid == gSystemServerPid | 查 init zygote restart 与 onrestart |
zygote 反复 restarting | init Service::Reap | crash_count_、time_crashed_ | 检查 critical、fatal 节流和启动失败 |
| 新 SystemServer 启动但计数持续增加 | SystemServer 构造函数 | mStartCount > 1 | 区分 runtime restart 与物理 reboot,回到启动场景判定 |
有两个常见误判。第一,sys.boot_completed=1 只说明某次启动曾到达 boot complete,不能替代 Zygote 的 gSystemServerPid 或 init 的 crash count。第二,sys.system_server.start_count 只统计当前 property 生命周期内的 SystemServer 实例,不告诉你根因是 Java、Watchdog 还是 native。
11. 源码验证
源码测试不能完整模拟真实 init、内核信号和 debuggerd,但可以验证状态转移中的局部契约。阅读时应把“测试断言覆盖的范围”和“必须依赖设备现场的部分”分开。
源码文件:frameworks/base/services/tests/servicestests/src/com/android/server/WatchdogTest.java
相关测试:WatchdogTest.blockedThread
@Test
public void blockedThread() {
// 输入:10ms 的 checker 超时,但被检查的 Handler 不回调。
mChecker.scheduleCheckLocked(TIMEOUT_MS);
assertEquals(Watchdog.WAITING,
mChecker.getCompletionStateLocked());
mClock.advanceBy(6);
// 断言:预警时间到达,只能收集信息,不应立即 kill。
assertEquals(Watchdog.WAITED_UNTIL_PRE_WATCHDOG,
mChecker.getCompletionStateLocked());
mClock.advanceBy(6);
// 断言:完整 timeout 后才进入 OVERDUE。
assertEquals(Watchdog.OVERDUE,
mChecker.getCompletionStateLocked());
}这个用例用 TestClock 分两次推进时间:第一次到达 pre-watchdog 状态,第二次才到达 OVERDUE。它证明了时间状态机和本文所说的“先收集、后终止”;不证明真实设备上的线程 dump 内容或 kill 后的 init 重启。
ART Service 的测试则验证 staged artifact 失败和延迟提交,说明恢复链中“新进程已启动”不等于“所有启动期资源都已完成消费”。
源码文件:art/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
相关测试:testCommitPreRebootStagedFiles、testCommitPreRebootStagedFilesObsolete
when(mArtd.checkPreRebootStagedFilesStatus())
.thenReturn(TestingUtils.createPreRebootStagedFilesStatus(
true /* isCommittable */, 200 /* createdAtMillis */));
mArtManagerLocal.onBoot(
ReasonMapping.REASON_BOOT_AFTER_OTA,
null /* progressCallbackExecutor */,
null /* progressCallback */);
InOrder inOrder = inOrder(mArtd);
inOrder.verify(mArtd).deletePreRebootStagedMetadata();
// 本文注:开机阶段先提交 primary;BOOT_COMPLETED 后才提交 secondary。
verify(mArtd, times(1))
.commitPreRebootStagedFiles(any(), any());
simulateBroadcast(Intent.ACTION_BOOT_COMPLETED);
verify(mArtd, times(2))
.commitPreRebootStagedFiles(any(), any());isCommittable=true 的输入覆盖成功提交和生命周期延迟;对应的 testCommitPreRebootStagedFilesObsolete() 将输入改为 false,断言 cleanUpPreRebootStagedFiles() 被调用且从未 commit。它们不能证明 Zygote 或 init 的重启,但能防止把“启动恢复”叙述成一次无条件的全量资源恢复。
12. 阅读收束
可以用下面的顺序复述一次真正的 SystemServer crash:先指出根因属于 Java、Watchdog 还是 native;再说明当前进程怎样退出;然后指出 primary Zygote 如何通过 gSystemServerPid 判断必须自杀;最后追踪 init 的 Service::Reap、SVC_RESTARTING 和 HandleProcessActions() 如何重新拉起 Zygote。若失败连续发生,还要补上 critical 的 4 次阈值、boot-completed 条件和 24 小时 fatal reboot 节流。
做到这一步,就不会把三种不同的“计数”混在一起:sys.system_server.crash_java 是 Java 日志路径的计数,mStartCount 是当前 property 生命周期内的 SystemServer 实例数,init 的 crash_count_ 是某个 service 的非正常退出窗口。它们的 owner、持久性和消费者都不同,诊断结论也必须分别建立。
