如果进程死了,如何编写bash脚本来重启?

时间:2009-03-30 11:27:09

标签: bash scripting cron

我有一个python脚本,它将检查队列并对每个项目执行操作:

# checkqueue.py
while True:
  check_queue()
  do_something()

如何编写一个bash脚本来检查它是否正在运行,如果没有,请启动它。大致是以下伪代码(或者它应该像ps | grep那样做?):

# keepalivescript.sh
if processidfile exists:
  if processid is running:
     exit, all ok

run checkqueue.py
write processid to processidfile

我会从crontab中调用它:

# crontab
*/5 * * * * /path/to/keepalivescript.sh

9 个答案:

答案 0 :(得分:572)

避免使用PID文件,crons或任何其他试图评估不属于其子级的进程的文件。

有一个很好的理由为什么在UNIX中,你只能等待你的孩子。尝试解决这个问题的任何方法(ps解析,pgrep,存储PID,...)都是有缺陷的,并且在其中有漏洞。只需说

相反,您需要将监控流程的流程作为流程的父流程。这是什么意思?这意味着只有启动的过程才能可靠地等待它结束。在bash中,这绝对是微不足道的。

until myserver; do
    echo "Server 'myserver' crashed with exit code $?.  Respawning.." >&2
    sleep 1
done

上面的bash代码在myserver循环中运行until。第一行开始myserver并等待它结束。结束时,until会检查其退出状态。如果退出状态为0,则表示它正常结束(这意味着您要求它以某种方式关闭,并且它成功完成)。在这种情况下,我们不想重新启动它(我们只是要求它关闭!)。如果退出状态为 0until将运行循环体,它会在STDERR上发出错误消息并重新启动循环(返回第1行) 1秒后

为什么我们等一下?因为如果myserver的启动顺序出现问题并且它立即崩溃,你将会有一个非常密集的循环,不断重启并崩溃。 sleep 1消除了那种压力。

现在您需要做的就是启动此bash脚本(可能是异步),它将监视myserver并根据需要重新启动它。如果要在启动时启动监视器(使服务器“生存”重新启动),可以使用@reboot规则在用户的cron(1)中安排它。使用crontab打开您的cron规则:

crontab -e

然后添加规则以启动监控脚本:

@reboot /usr/local/bin/myservermonitor

可替换地;看一下inittab(5)和/ etc / inittab。您可以在其中添加一行以使myserver在某个初始级别启动并自动重新生成。


编辑。

让我添加一些有关使用PID文件的信息。虽然他们很受欢迎;它们也非常有缺陷,没有理由不以正确的方式做到这一点。

考虑一下:

  1. PID回收(杀死错误的进程):

    • /etc/init.d/foo start:启动foo,将foo的PID写入/var/run/foo.pid
    • 片刻之后:foo以某种方式死亡。
    • 片刻之后:任何启动的随机进程(称之为bar)都会采用随机PID,假设它采用foo的旧PID。
    • 您注意到foo已消失:/etc/init.d/foo/restart读取/var/run/foo.pid,检查它是否还活着,找到bar,认为它是foo,杀了它,开始一个新的foo
  2. PID文件过时了。您需要过于复杂(或者我应该说,非平凡)的逻辑来检查PID文件是否过时,并且任何此类逻辑再次容易受到1.的攻击。<​​/ p>

  3. 如果您甚至没有写入权限或处于只读环境,该怎么办?

  4. 这是毫无意义的过度复杂化;看看我上面的例子有多简单。根本不需要复杂化。

  5. 另请参阅:Are PID-files still flawed when doing it 'right'?

    顺便说一下; 比PID文件更糟糕的是解析ps不要这样做。

    1. ps非常难以移植。虽然你几乎在每个UNIX系统上都能找到它;如果你想要非标准输出,它的参数差别很大。标准输出仅供人类使用,而不是脚本解析!
    2. 解析ps导致大量误报。以ps aux | grep PID示例为例,现在想象有人在某个地方启动一个带有数字的进程作为参数,它恰好与你守护你的守护进程的PID相同!想象一下,两个人开始一个X会话,你贪图X杀死你的。这只是各种各样的坏事。
    3. 如果您不想自己管理流程;有一些非常好的系统可以作为您的流程的监控器。例如,查看runit

答案 1 :(得分:23)

看看monit(http://mmonit.com/monit/)。它处理脚本的启动,停止和重启,并且可以在必要时执行运行状况检查和重新启动。

或者做一个简单的脚本:

while true
do
/your/script
sleep 1
done

答案 2 :(得分:8)

最简单的方法是在文件中使用flock。在Python脚本中你要做

lf = open('/tmp/script.lock','w')
if(fcntl.flock(lf, fcntl.LOCK_EX|fcntl.LOCK_NB) != 0): 
   sys.exit('other instance already running')
lf.write('%d\n'%os.getpid())
lf.flush()

在shell中你可以测试它是否正在运行:

if [ `flock -xn /tmp/script.lock -c 'echo 1'` ]; then 
   echo 'it's not running'
   restart.
else
   echo -n 'it's already running with PID '
   cat /tmp/script.lock
fi

但是当然你不必测试,因为如果它已经运行并且你重新启动它,它将以'other instance already running'退出

当进程死亡时,它的所有文件描述符都将被关闭,并且所有锁都会被自动删除。

答案 3 :(得分:6)

你应该使用monit,这是一个标准的unix工具,可以监控系统中的不同内容并做出相应的反应。

来自文档:http://mmonit.com/monit/documentation/monit.html#pid_testing

check process checkqueue.py with pidfile /var/run/checkqueue.pid
       if changed pid then exec "checkqueue_restart.sh"

您还可以配置monit,以便在重新启动时通过电子邮件发送给您。

答案 4 :(得分:5)

if ! test -f $PIDFILE || ! psgrep `cat $PIDFILE`; then
    restart_process
    # Write PIDFILE
    echo $! >$PIDFILE
fi

答案 5 :(得分:2)

我不确定它在操作系统之间的可移植性,但是您可以检查系统是否包含“运行一”命令,即“ man run-one”。 具体来说,这组命令包括“恒定运行”,这似乎正是所需的。

从手册页:

  

连续运行命令[ARGS]

注意:显然,这可以从您的脚本中调用,但也完全不需要脚本。

答案 6 :(得分:1)

我在许多服务器上使用了以下脚本并取得了巨大成功:

pid=`jps -v | grep $INSTALLATION | awk '{print $1}'`
echo $INSTALLATION found at PID $pid 
while [ -e /proc/$pid ]; do sleep 0.1; done

注释:

  • 它正在寻找一个java进程,所以我 可以使用jps,这是更多 分布比一致 PS
  • $INSTALLATION包含足够的过程路径,这是完全明确的
  • 在等待进程死亡时使用sleep,避免占用资源:)

这个脚本实际上用于关闭正在运行的tomcat实例,我想在命令行关闭(并等待),所以将它作为子进程启动对我来说根本不是一个选项。 / p>

答案 7 :(得分:0)

我将其用于我的npm进程

#!/bin/bash
for (( ; ; ))
do
date +"%T"
echo Start Process
cd /toFolder
sudo process
date +"%T"
echo Crash
sleep 1
done

答案 8 :(得分:0)

在线:

while true; do <your-bash-snippet> && break; done

例如

while true; do openconnect x.x.x.x:xxxx && break; done