问:
我有一个python脚本,它将检查队列并对每个元素执行操作:
代码语言:javascript复制# checkqueue.py
while True:
check_queue()
do_something()
我如何编写一个bash脚本来检查它是否正在运行,如果没有,则启动它。大致如下伪代码(或者它应该做一些类似 ps | grep 的事情?)
代码语言:javascript复制# keepalivescript.sh
if processidfile exists:
if processid is running:
exit, all ok
run checkqueue.py
write processid to processidfile
代码语言:javascript复制我将从crontab中调用它:
代码语言:javascript复制# crontab
*/5 * * * * /path/to/keepalivescript.sh
答:
避免使用 PID 文件、cron 或其他任何试图评估不属于其子进程的进程。
在UNIX中,有充分的理由只能等待子进程。任何试图解决这个问题的方法(ps解析、pgrep、存储PID等)都是有缺陷的,其中存在漏洞。待后文分析。
假设你的进程名为procA,监控它的进程名为procB,则需要procB成为procA的父进程。因为只有启动你的进程的进程才能可靠地等待它结束。而这在Bash中很容易实现。
代码语言:javascript复制until procA; do
echo "procA crashed with exit code $?. Restart..." >&2
sleep 1
done
上面的bash代码在一个until循环中运行procA。第一行启动procA并等待它结束。当它结束时,until检查其退出状态。如果退出状态为0,则表示它正常结束(这意味着你要求它以某种方式关闭,并且它成功关闭了)。在这种情况下,我们不想重新启动它(我们只是要求它关闭!如果退出状态不是0,until将运行循环体,该循环体在STDERR上发出错误消息,并在 1 秒后重新启动循环(返回第 1 行)。
我们为什么要等一会儿?因为如果procA的启动顺序出了问题并立即崩溃,你将得到一个非常密集的循环,不断重新启动和崩溃。sleep 1消除了这种压力。
然后需要做的就是启动这个bash脚本,它将监控procA并在必要时重新启动它。如果你想在(操作系统)启动时启动监控脚本,你可以用@reboot规则在用户的 cron(1) 中调度它。使用crontab -e命令打开你的cron规则,然后添加一个规则来启动你的监控脚本:
代码语言:javascript复制@reboot /usr/local/bin/procAmonitor
至于不使用PID文件的理由: 1. PID重用(可能导致杀死错误的进程)。 2. PID文件过时。你需要过于更复杂的逻辑来检查PID文件是否过时,而任何这样的逻辑都同样有1中的缺陷。 3. 如果你甚至没有写访问权限或者处于只读环境中该怎么办?
或者,查看systemd.unit(5)。你可以在/lib/systemd/system目录中添加一个名为procA.service的配置文件,让systemd进程监控你的procA。
代码语言:javascript复制[Unit]
Description=Daemon for procA.
[Service]
ExecStart=/path/to/procA
Restart=on-failure
RestartSec=1s
[Install]
WantedBy=multi-user.target
然后再执行以下命令即可:
代码语言:javascript复制systemctl daemon-reload
systemctl enable procA.service
代码语言:javascript复制
参考:
- stackoverflow question 696839
- man systemd.unit
- man systemctl