我可以检测到从长期运行的后台进程中提前退出吗?

时间:2013-10-30 06:00:25

标签: linux bash process timeout

我正在尝试改进在群集环境中运行的多个服务器的启动脚本。服务器进程应无限期运行,但偶尔会在启动时失败,例如Address already in use例外。

我希望启动脚本的退出代码反映这些早期终止,比如等待1秒并告诉我服务器是否似乎已经开始好了。我还需要服务器PID回显。

到目前为止,这是我最好的镜头:

$ cat startup.sh
# start the server in the bg but if it fails in the first second, 
# then kill startup.sh.

CMD="start_server -option1 foo -option2 bar"
eval "($CMD >> cc.log 2>&1 || kill -9 $$ &)"
SERVER_PID=$!

# the `kill` above only has 1 second to kill me-- otherwise my exit code is 0
sleep 1
echo $SERVER_PID

退出代码工作正常但仍存在两个问题:

  1. 如果服务器长时间运行但最终遇到错误,则父startup.sh已经退出,$$ PID可能已被不相关的进程重用,该脚本将然后杀掉。

  2. SERVER_PID不正确,因为它是子shell的PID而不是start_server命令(在这种情况下是startup.sh脚本的孙。 / p>

  3. 是否有更简单的方法来处理start_server进程,获取其PID,并使用超时检查错误代码?我查看了bash builtins waittimeout,但它们似乎不适用于最终不应退出的进程。

    我无法更改服务器代码,启动脚本无限期运行。

2 个答案:

答案 0 :(得分:1)

你也可以使用coproc(看看,我把命令放在一个数组中,还有正确的引用!):

#!/bin/bash
cmd=( start_server -option1 foo -option2 bar )
coproc mycoprocfd { "${cmd[@]}" >> cc.log 2>&1 ; }
server_pid=$!
sleep 1
if [[ -z "${mycoprocfd[@]}" ]]; then
    echo >&2 "Failure detected when starting server! Server died before 1 second."
    exit 1
else
    echo $server_pid
fi

诀窍是coproc将stdin和stdout重定向的文件描述符放在指定的数组(这里是mycoprocfd)中,并在进程退出时清空数组。所以你不需要用PID本身做笨拙的东西。

因此,您可以检查服务器从不退出:

#!/bin/bash
cmd=( start_server -option1 foo -option2 bar )
coproc mycoprocfd { "${cmd[@]}" >> cc.log 2>&1 ; }
server_pid=$!
read -u "${mycoprocfd[0]}"
echo >&2 "Oh dear, the server with PID $server_pid died after $SECONDS seconds."
exit 1

那是因为read会读取coproc给出的文件描述符(但是这里没有读过任何内容,因为你的命令的stdout已被重定向到一个文件!),当文件描述符是关闭,即coproc启动的命令退出时。

我会说这是一个非常优雅的解决方案!

现在,只要coproc存在,这个脚本就会存在。我明白这不是你想要的。在这种情况下,您可以使用-t选项超时读取,然后如果超时,您将使用返回退出状态大于128的事实。例如,超时4.5秒

#!/bin/bash
timeout=4.5
cmd=( start_server -option1 foo -option2 bar )
coproc mycoprocfd { "${cmd[@]}" >> cc.log 2>&1 ; }
server_pid=$!
read -t $timeout -u "${mycoprocfd[0]}"
if (($?>128)); then
    echo "$server_pid <-- all is good, it's still alive after $timeout seconds."
else
    echo >&2 "Oh dear, the server with PID $server_pid died after $timeout seconds."
    exit 1
fi
exit 0 # Yay

这也非常优雅:)

使用,扩展和适应您的需求! (但是有良好的做法!)

希望这有帮助!

<强>说明

  • coproc是bash-builtin,出现在bash 4.0中。这里显示的解决方案是100%纯粹的bash(除了第一个,sleep,这不是最好的!)。
  • 在脚本中使用coproc几乎总是优于使用&将作业放在后台,并且在睡眠和检查$!时做一些笨拙和笨拙的事情。
  • 如果你希望coproc保持安静,无论发生什么事情(例如,如果启动命令时出错,由于你自己处理了所有问题,这很好),请执行以下操作:

    coproc mycoprocfd { "${cmd[@]}" >> cc.log 2>&1 ; } > /dev/null 2>&1
    

答案 1 :(得分:0)

20分钟的更多Google搜索显示https://stackoverflow.com/a/6756971/494983kill -0 $PID来自https://stackoverflow.com/a/14296353/494983

所以我似乎可以使用:

$ cat startup.sh   
CMD="start_server -option1 foo -option2 bar"
eval "$CMD >> cc.log 2>&1 &"
SERVER_PID=$!
sleep 1
kill -0 $SERVER_PID
if [ $? != 0 ]; then
    echo "Failure detected when starting server! PID $SERVER_PID doesn't exist!" 1>&2
    exit 1
else
    echo $SERVER_PID
fi

对于我无法发送信号但在我的情况下运行良好的进程(startup.sh启动服务器本身),这不适用。