Linux后台任务异常退出需捕获信号与退出码:用trap注册INT/TERM/ERR/EXIT信号处理,记录上下文;启动子进程后保存PID并wait获取真实状态;结合systemd或nohup提升可观测性;加锁文件看门狗防假存活。

Linux 后台任务异常退出时,不能只靠日志“猜”原因,关键是要让退出本身留下可解析的状态线索——既要捕获是否被信号杀死,也要拿到真实退出码,并确保清理动作不遗漏。
用 trap 捕获关键信号并记录退出上下文
在脚本开头就注册 trap,覆盖常见中断和终止场景:
- INT 和 TERM:对应 Ctrl+C 或 kill 命令,用 exit 130(128+2)或 143(128+15)退出,符合 POSIX 约定,方便后续识别
- ERR:配合 set -e 使用,在任意命令失败时触发清理,但注意它不捕获管道中间失败,需加 set -o pipefail
- EXIT:必须保留,它是兜底机制,无论成功、失败、被杀都会执行,适合做最终资源释放
示例写法:
trap 'echo "[$(date)] EXITED by SIGTERM (code 143)" >> /var/log/task.log; cleanup; exit 143' TERM注意:trap 内部避免调用可能阻塞的命令(如未配置好的 logger),日志写入建议用绝对路径 + 显式重定向,比如 /bin/echo "...">>/var/log/task.log 2>&1。
启动子进程后保存 PID 并在退出前 wait 获取真实状态
后台任务常以子进程形式运行(如 ./worker.sh &),若不主动 wait,父脚本无法知道它怎么结束的。
- 启动时立即记录 PID:./worker.sh & WORKER_PID=$!
- 在 cleanup 函数中检查该 PID 是否还存活:kill -0 "$WORKER_PID" 2>/dev/null && wait "$WORKER_PID"
- wait 返回后,$? 就是子进程真实退出状态:若为 0–127 是正常退出码;若 ≥128,则表示被信号终止(如 137 = 128+9 → SIGKILL)
这样就能区分“程序自己报错退出”和“被 OOM Killer 杀掉”等不同情况。
结合 systemd 或 nohup 提升可观测性
纯 shell 脚本容易丢失上下文,建议分层加固:
- 用 nohup ./task.sh >> /var/log/task.log 2>&1 & 避免终端断开导致 SIGHUP 终止
- 若用 systemd 管理,unit 文件中启用 Restart=on-failure 并设置 RestartSec=5,同时加 StandardOutput=journal,便于用 journalctl -u task.service -n 50 快速定位
- 查异常退出时,先看 systemctl status task.service 中的 “Killed by signal” 或 “Process exited with code”,再查 dmesg 确认是否 OOM
加轻量级看门狗防“假存活”
有些任务卡死却不退出,日志停更但进程还在,系统误判为正常。简单防住的方法:
- 脚本启动时写锁文件:echo $$ > /tmp/task.pid
- 正常退出前删掉:rm -f /tmp/task.pid
- 另起一个定时检查脚本,发现锁文件存在超 10 分钟且无更新,就报警并 kill
这种机制不依赖进程内部逻辑,能有效捕获僵死状态。











