我能否检测到长时间运行的后台进程提前退出



我正在尝试改进在群集环境中运行的多台服务器的启动脚本。服务器进程应无限期运行,但偶尔会在启动时失败,例如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不正确,因为它是子外壳的 PID,而不是 start_server 命令(在本例中是startup.sh脚本的孙子)。

有没有更简单的方法来后台处理start_server过程,获取其PID,并使用超时检查错误代码? 我研究了 bash 内置的 waittimeout 但它们似乎不适用于最终不应该退出的进程。

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

你也可以使用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 选项使读取超时,然后您将使用 return 的退出状态大于 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 4.0 中的 bash 内置。这里显示的解决方案是 100% 纯 bash(除了第一个,带有 sleep ,这根本不是最好的!
  • 在脚本中使用coproc几乎总是优于将作业放在后台&,并在睡眠和检查$!时做笨拙而笨拙的事情。
  • 如果你想让coproc保持沉默,无论发生什么(例如,如果启动命令时出错,这很好,因为你自己处理所有事情),请执行:

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

20分钟的谷歌搜索揭示了 https://stackoverflow.com/a/14296353/494983 的 https://stackoverflow.com/a/6756971/494983 和kill -0 $PID

所以似乎我可以使用:

$ 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启动服务器本身)运行良好。

相关内容

  • 没有找到相关文章

最新更新