服务重启策略
本文面向已经读过 服务启动流程、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
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
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
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
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
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.cppService::RestartStopOrReset
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.cppStopResetStopOrReset
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
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.cppParseOneshotservice.cppReap
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
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.cppParseRebootOnFailureservice.cppReap
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
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. 状态观测
| 观察 | 可以证明 | 不能证明 |
|---|---|---|
stopping | init 已发起停止或 timeout 清理 | 子进程已经退出 |
stopped | Reap 判定不会进入当前自动重启,或服务已被停止 | 进程组没有残留 |
restarting | Reap 已登记 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. 重启验证
排查“服务不断重启”时按顺序记录:
- 读取
stopping → stopped/restarting日志,确认是主动 Stop、Timeout 还是 SIGCHLD; - 查看退出码和信号,判断
was_last_exit_ok_; - 检查 oneshot、disabled、reset、restart flags 是否改变了 Reap 分支;
- 计算
time_started_ + restart_period(),确认是否正在等待 5 秒异常限速或配置周期; - 单独核对 onrestart 命令的成功/失败,不把依赖重启当作 Service 本身成功;
- 若是 critical 或 reboot_on_failure,确认触发的是累计 fatal 还是单次 failure reboot;
- 最后检查新 pid、
init.svc、socket 和业务协议,把退出、重启与业务恢复放在同一时间线上。
本文证明了 Android 17 服务退出后的 Reap 状态机、restart period 调度、主动 restart/stop/reset、oneshot、gentle_kill、critical、reboot_on_failure 和 onrestart 关系;没有证明特定设备的崩溃原因、SELinux 规则、依赖服务恢复结果、Binder/HAL ready 或业务稳定性。
