挂起进程指仍在运行但停止处理任务、不更新状态、不写日志、不响应心跳,其最直观特征是CPU和I/O长期静默,使用率持续为0%或极低。

检测守护进程是否挂起,关键不是等它“彻底死掉”,而是识别它是否还在有效响应——挂起(hung)不等于退出,进程可能仍在运行,但已停止处理任务、不更新状态、不写日志、不响应心跳。
看资源行为:CPU 和 I/O 是否长期静默
挂起进程最直观的特征是“活着却不干活”:
- CPU 使用率持续为 0%(或极低,如
- 无磁盘 I/O(
iotop -p PID或/proc/PID/io中rchar/wchar几乎不增长) - 无网络收发(
ss -tulpn | grep PID显示连接存在但netstat -s对应协议计数停滞)
查进程内部状态:/proc/PID/status 的线索
Linux 内核通过 /proc/PID/status 暴露关键指标:
-
State: 若显示
T (stopped)或t (traced),说明被信号暂停(如kill -STOP) - voluntary_ctxt_switches / nonvoluntary_ctxt_switches: 长时间无自愿上下文切换(比如 PHP CLI 脚本本该每秒轮询一次数据库,却连续 5 分钟没变),高度可疑
- last_run_time / last_cpu:(需内核开启调度调试)可辅助判断是否被长期剥夺 CPU
加主动心跳:让脚本自己“报平安”
依赖外部观察有延迟,更可靠的是脚本内置轻量级心跳机制:
- 每次主循环完成时,用
file_put_contents('/tmp/myapp.heartbeat', time())更新时间戳 - 另起一个监控脚本(如每 30 秒执行一次),检查该文件是否在 90 秒内被更新;超时即判定挂起
- 避免单点故障:心跳文件可写入 tmpfs(
/dev/shm/),防止磁盘满导致误判
用 supervisor 配合 exitcodes 和 heartbeat 日志
supervisord 本身不检测挂起,但可强化反馈链:
- 在 PHP 脚本中,定期调用
posix_kill(getmypid(), 0)自检,失败则exit(99) - 配置
exitcodes=0,99并设autorestart=true,使挂起后能触发重启 - 启用
stdout_logfile=/var/log/myapp.log+loglevel=info,确保每轮循环至少输出一行带时间戳的标记(如[2026-06-09 05:02:10] loop #1248),缺失即告警

















