Skip to content

服务重启策略

追踪服务退出、Reap、onrestart、restart_period、oneshot、disabled、critical 和下一次 Start 的真实状态机。

基于android-17.0.0_r1
AndroidinitServicerestartcrash

服务重启策略 ​

本文面向已经读过 服务启动流程、Service对象 和 Zygote服务 的读者。本文不再解释 fork/exec 的细节,而是回答“进程退出后谁决定下一步”:Reap() 如何区分正常退出、异常退出、oneshot、disabled、reset 和手动 restart,restart_period 何时生效,onrestart 的消费者是谁,以及 critical/reboot_on_failure 在什么条件下改变系统级结果。

读完后,读者应能把 init.svc.foo 的 stopped/restarting 日志与源码状态对应起来,知道一次退出是否会重新 Start(),为什么配置 restart_period 0 仍可能有 5 秒异常退出限速,并能区分“服务被 init 主动停止”“服务自己崩溃”“服务超时被杀”“critical 触发 fatal”这几条不同路径。

1. 状态机 ​

服务重启不是 Reap() 内立即 fork。退出后 Service 先清理资源、更新 flags、执行 onrestart,再由 init 主循环的 HandleProcessActions() 根据时间重新调用 Start()。

图中的 reap 是源码中的处理阶段,不是长期暴露给 init.svc.* 的状态。它同时承担进程组清理、socket 删除、退出结果记录、崩溃计数和重启决策。

2. 退出入口 ​

2.1 退出结果 ​

源码文件:system/core/init/service.cpp

相关函数/类型:Service::Reap

cpp
was_last_exit_ok_ = siginfo.si_code == CLD_EXITED && siginfo.si_status == 0;

if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART) && !(flags_ & SVC_RESET)) {
    flags_ |= SVC_DISABLED;
}

if (flags_ & (SVC_DISABLED | SVC_RESET)) {
    NotifyStateChange("stopped");
    return;
}

只有 CLD_EXITED 且退出码为 0 才记为正常退出;信号终止和非零退出都进入异常语义。oneshot 退出后默认增加 disabled,除非存在手动 restart 或 reset 标志;已经 disabled 或 reset 的服务直接 stopped,不会进入自动重启段。

2.2 资源清理 ​

源码文件:system/core/init/service.cpp

相关函数/类型:Service::Reap

cpp
if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
    KillProcessGroup(SIGKILL);
} else if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
    KillProcessGroup(SIGKILL);
}

for (const auto& socket : sockets_) {
    if (socket.persist) continue;
    unlink((ANDROID_SOCKET_DIR "/" + socket.name).c_str());
}

if (flags_ & SVC_EXEC) UnSetExec();
if (flags_ & SVC_TEMPORARY) return;

Android R 以后,即使 oneshot 也会在 reap 时杀掉残留进程组;非持久 socket 会被删除。temporary exec 在清理 socket 和 exec 标志后直接离开普通 Service 状态机,因此不能套用普通服务的“崩溃后重启”结论。

3. 重启条件 ​

3.1 Reap提交 ​

源码文件:system/core/init/service.cpp

相关函数/类型:Service::Reap

cpp
flags_ &= (~SVC_RESTART);
flags_ |= SVC_RESTARTING;

// Execute all onrestart commands for this service.
onrestart_.ExecuteAllCommands();

NotifyStateChange("restarting");
return;

进入这段代码意味着该 Service 没有被 disabled/reset/oneshot 终止,也没有因 temporary 直接返回。onrestart 在这里同步调用其 Action 命令;它不是一个新的 service start 请求,也不会绕过 restart period 立即生成第二个进程。

3.2 时间调度 ​

源码文件:system/core/init/init.cpp

相关函数/类型:HandleProcessActions

cpp
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;
    }
}

HandleProcessActions() 每轮扫描 ServiceList:到时间才调用 Start,未到时间则把最早的 restart time 返回给主循环用于 epoll timeout。重启的调度 owner 是 init 主循环,不是 SIGCHLD handler 本身。

3.3 基准时间 ​

源码文件:system/core/init/service.h

相关函数/类型:Service::restart_period

cpp
std::chrono::seconds restart_period() const {
    if (!was_last_exit_ok_) {
        return std::max(restart_period_, default_restart_period_);
    }
    return restart_period_;
}

restart_period_ 是 rc 配置值,default_restart_period_ 是 5 秒。正常退出使用配置周期;异常退出或 timeout 使用二者较大值,防止崩溃服务用 restart_period 0 形成高速重启风暴。

4. 主动控制 ​

4.1 restart命令 ​

相关源码:

  • system/core/init/service.cpp
  • Service::Restart
  • StopOrReset
cpp
void Service::Restart() {
    if (flags_ & SVC_RUNNING) {
        StopOrReset(SVC_RESTART);
    } else if (!(flags_ & SVC_RESTARTING)) {
        if (auto result = Start(); !result.ok()) {
            LOG(ERROR) << "Could not restart '" << name_ << "': " << result.error();
        }
    }
}

if (how == SVC_RESTART) {
    flags_ &= ~(SVC_DISABLED | SVC_RESET);
} else {
    flags_ &= ~SVC_RESTART;
}

运行中的服务先设置 restart 意图并杀进程,等待 Reap;已经停止且不在 restarting 的服务则直接 Start。SVC_RESTART 的作用是让 oneshot 在停止过程中也能被重新启动,而不是把停止和启动变成并行操作。

4.2 stop与reset ​

相关源码:

  • system/core/init/service.cpp
  • Stop
  • Reset
  • StopOrReset
cpp
void Service::Reset() {
    StopOrReset(SVC_RESET);
}

void Service::Stop() {
    StopOrReset(SVC_DISABLED);
}

if (how == SVC_RESET) {
    flags_ |= (flags_ & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;
} else {
    flags_ |= how;
}

Stop() 设置 disabled,阻止后续 class 自动启动;Reset() 对没有 rc disabled 的服务设置 reset,使其停止但保留再次按 class 启动的可能。若 rc 中本来有 disabled,reset 仍保留 disabled,这不是“reset 永远清除 disabled”。

4.3 gentle kill ​

源码文件:system/core/init/service.cpp

相关函数/类型:StopOrReset

cpp
if (pid_) {
    if (flags_ & SVC_GENTLE_KILL) {
        KillProcessGroup(SIGTERM);
        if (!process_cgroup_empty()) std::this_thread::sleep_for(200ms);
    }
    KillProcessGroup(SIGKILL);
    NotifyStateChange("stopping");
}

默认 Stop/Restart 最终使用 SIGKILL;只有 gentle_kill 才先给进程组 SIGTERM,并最多等待 200ms。stopping 是通知,不是子进程已经退出;最终状态仍由 SIGCHLD → Reap 决定。

5. 特殊策略 ​

5.1 oneshot ​

相关源码:

  • system/core/init/service_parser.cpp
  • ParseOneshot
  • service.cpp
  • Reap
cpp
Result<void> ServiceParser::ParseOneshot(std::vector<std::string>&& args) {
    service_->flags_ |= SVC_ONESHOT;
    return {};
}

oneshot 不是“只允许启动一次”的永久锁,而是退出后默认进入 disabled;显式 restart 设置 SVC_RESTART 时仍可重启。exec 创建的 temporary oneshot 另有临时对象清理语义。

5.2 critical ​

源码文件:system/core/init/service_parser.cpp

相关函数/类型:ParseCritical

cpp
if (arg[0] == "target") {
    fatal_reboot_target = arg[1];
} else if (arg[0] == "window") {
    auto window = ExpandProps(arg[1]);
    if (!window.ok()) return window.error();
    if (*window == "off") return {};
    int minutes;
    if (!ParseInt(*window, &minutes, 0)) {
        return Error() << "critical: 'fatal_crash_window' must be an integer > 0";
    }
    fatal_crash_window = std::chrono::minutes(minutes);
}

Android 17 的 critical 参数是 target=... 和 window=... 形式;崩溃次数阈值并不从 rc 读取。Reap() 在 critical 服务异常退出超过 4 次时调用 fatal reboot target;启动完成前的崩溃也进入该检查。不能使用旧稿的 critical 60 4 作为 Android 17 示例。

5.3 failure reboot ​

相关源码:

  • system/core/init/service_parser.cpp
  • ParseRebootOnFailure
  • service.cpp
  • Reap
cpp
if (service_->on_failure_reboot_target_) {
    return Error() << "Only one reboot_on_failure command may be specified";
}
if (!StartsWith(args[1], "shutdown") && !StartsWith(args[1], "reboot")) {
    return Error() << "reboot_on_failure commands must begin with either 'shutdown' or 'reboot'";
}
service_->on_failure_reboot_target_ = std::move(args[1]);

该策略与 critical 不同:它针对一次非零/信号退出,在 Reap 中直接触发预先配置的 shutdown/reboot 命令;不会等待累计 4 次,也不负责普通 restart period。

6. 重启动作 ​

6.1 Zygote示例 ​

源码文件:system/core/rootdir/init.zygote64.rc

相关函数/类型:service zygote

text
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

这些命令由 zygote Service 的 onrestart_ Action 消费。它们是重启副作用编排,不证明每个依赖服务都成功恢复;例如 --only-if-running 会保留下游服务原本未运行的状态意图。

6.2 动作失败 ​

onrestart_.ExecuteAllCommands() 逐条调用 Action 命令并记录失败,但 Service 不会因为某一条 onrestart 命令失败而自动回滚此前命令,也不会取消自身的 restarting 状态。排障必须分别查看 Service 重启和每条 onrestart 命令日志。

7. 状态观测 ​

观察可以证明不能证明
stoppinginit 已发起停止或 timeout 清理子进程已经退出
stoppedReap 判定不会进入当前自动重启,或服务已被停止进程组没有残留
restartingReap 已登记 restarting 并执行 onrestart下一次 Start 已成功
init.svc.<name> 再次 running新一轮 Start 完成 init 侧提交新进程 exec 和业务 ready
critical fatal 日志达到源码中的崩溃计数条件所有下游服务已恢复

8. 重启测试 ​

8.1 解析测试 ​

init_test.cpp::RejectsCriticalAndOneshotService 构造同时含 critical、oneshot 和 user 的 rc,断言 Android R+ 解析错误;它证明 parser 约束,不证明崩溃计数路径。

8.2 停止路径 ​

init_test.cpp::GentleKill 以 root 启动带 gentle_kill 的 oneshot 服务,用 strace 观察 SIGTERM,再调用 Stop,断言日志包含 killed by SIGTERM。它证明优雅停止先发送 SIGTERM,不证明服务一定能在 200ms 内自行退出。

8.3 cgroup回收 ​

service_test.cpp::ServiceStopTest 覆盖原始 cgroup 目录被迁移/删除后的 Stop,证明进程组清理不依赖单一旧路径;它不证明崩溃后的 restart policy。

9. 重启验证 ​

排查“服务不断重启”时按顺序记录:

  1. 读取 stopping → stopped/restarting 日志,确认是主动 Stop、Timeout 还是 SIGCHLD;
  2. 查看退出码和信号,判断 was_last_exit_ok_;
  3. 检查 oneshot、disabled、reset、restart flags 是否改变了 Reap 分支;
  4. 计算 time_started_ + restart_period(),确认是否正在等待 5 秒异常限速或配置周期;
  5. 单独核对 onrestart 命令的成功/失败,不把依赖重启当作 Service 本身成功;
  6. 若是 critical 或 reboot_on_failure,确认触发的是累计 fatal 还是单次 failure reboot;
  7. 最后检查新 pid、init.svc、socket 和业务协议,把退出、重启与业务恢复放在同一时间线上。

本文证明了 Android 17 服务退出后的 Reap 状态机、restart period 调度、主动 restart/stop/reset、oneshot、gentle_kill、critical、reboot_on_failure 和 onrestart 关系;没有证明特定设备的崩溃原因、SELinux 规则、依赖服务恢复结果、Binder/HAL ready 或业务稳定性。