Linux下程序崩溃后应交由systemd管理,通过Restart=always、RestartSec=1、StartLimitIntervalSec=60和StartLimitBurst=5等配置实现安全自动重启,并按错误类型区分重试策略,如网络请求中ECONNREFUSED可重试而HTTP 400不可重试。

程序崩溃后如何自动重启进程
Linux 下最直接的方式是用守护脚本配合 systemd 或简单 shell 循环,但要注意避免无限 fork 和资源耗尽。不建议在 C++ 里自己 fork() + exec() 做双重守护——出错点太多,比如信号继承、文件描述符泄漏、子进程僵死。
推荐做法:把主程序做成无守护的普通进程,交由 systemd 管理。写一个 .service 文件,关键配置项包括:
-
Restart=always(进程退出即重启) -
RestartSec=1(失败后 1 秒再试,防高频闪退) -
StartLimitIntervalSec=60和StartLimitBurst=5(60 秒内最多启动 5 次,超限则 stop) -
Environment="LD_PRELOAD=/path/to/your_crash_hook.so"(可选,用于注入 crash 日志)
这样既干净又可控,systemctl status your-app 能直接看到重启次数和最近日志。
关键任务失败后怎么安全重试
重试不是简单套个 while 循环,重点是控制边界、避免雪崩、区分错误类型。比如网络请求失败,ECONNREFUSED 可以重试,HTTP 400 就不该重试。
立即学习“C++免费学习笔记(深入)”;
实操建议:
- 用指数退避:
sleep(1 ,上限设为 8 秒(即最多重试 4 次:1s → 2s → 4s → 8s) - 每次重试前检查是否还该继续:调用
std::this_thread::sleep_for()前先判断shutdown_requested标志位 - 记录失败原因:把
errno或异常字符串写进日志,否则重试 10 次都失败,你根本不知道卡在哪 - 不要在构造函数或析构函数里重试——异常路径下状态不可靠
示例片段:
for (int i = 0; i < max_retries; ++i) {
if (auto res = do_something(); res.ok()) return res;
if (!is_retryable(res.error_code())) break;
if (i < max_retries - 1) std::this_thread::sleep_for(std::chrono::seconds(1 << i));
}如何捕获未处理异常并触发自愈动作
C++ 默认未捕获异常会调用 std::terminate(),此时栈已开始销毁,不能再做复杂操作。想记录日志或发告警,必须提前拦截。
正确姿势:
- 全局设置
std::set_terminate(),里面只做最小动作:写入紧急日志文件(用write()系统调用,绕过 C 运行时)、触发信号(如raise(SIGUSR2))、然后调用_exit(128 + SIGABRT) - 不要在 terminate handler 里调用
std::cout、malloc、std::string构造——它们可能已失效 - 配合
signal(SIGABRT, ...)捕获崩溃信号,但注意:signal()处理器里只能调用 async-signal-safe 函数(write、_exit、siglongjmp等)
真正“自愈”不在 terminate 里做,而靠外部机制(如 systemd 自动拉起)完成。
共享内存或文件锁冲突导致初始化失败怎么办
很多“自愈”逻辑卡在启动阶段,比如尝试打开已存在的 /dev/shm/myapp_state 失败,或 flock() 返回 EWOULDBLOCK。这不是程序 bug,而是上一次没正常退出留下的残留。
应对策略:
- 启动时检测共享内存段是否存在且有效:用
shm_open()+fstat()查看 mtime 或自定义 magic 字段 - 文件锁场景下,加超时:用
fcntl(fd, F_SETLK, &lock)失败后,usleep(100000)再试,最多 3 次;超过就认为旧进程僵死,主动unlink()锁文件(需确保其他实例也遵守同一协议) - 避免依赖“清理代码”:不要指望
atexit()或全局对象析构能删掉 shm —— crash 时它们根本不会执行
真正健壮的做法是把初始化失败当作常态来设计,而不是当成异常。
自愈不是让程序在崩溃现场强行续命,而是快速失败 + 可控重启 + 状态隔离。最难的部分永远不是代码怎么写,而是界定哪些状态必须清零、哪些可以保留,以及怎么让上下游系统理解“这次重启不是故障,而是预期行为”。


















