我有许多与以下非常相似的 shell 脚本这个问题。每个脚本都会启动一些后台任务,用 记住它们的 PID $!
,进行一些测试,然后终止后台进程。
我的问题是我想运行大量此类脚本。如果我手动逐个运行它们,它们可以正常工作:ps -a
显示后台进程已被正确终止。
$ ./test_1.sh
$ ./test_2.sh
但是,如果我像这样在一个命令中运行它们:
$ ./test_1.sh; ./test_2.sh
由 启动的后台进程test_1.sh
在 启动时未能终止test_2.sh
。这会导致需要绑定的端口被占用test_2.sh
。因此,即使test_1.sh
发出了正确的kill
命令,test_2.sh
如果 紧接着 执行,也会崩溃test_1.sh
。
如果我在脚本之间插入延迟,则不会出现问题:
$ ./test_1.sh; sleep 1; ./test_2.sh
kill
因此,据推测,在发出信号和进程实际终止并释放端口之间存在延迟。
我的问题是:
- 发生了什么事?这个平台有特殊要求吗?我使用的是 Mac OS X。但对于最初在 Linux 上编写脚本的人来说,这似乎不是什么问题。
- 有没有更好的方法来确保后台进程已被终止,而无需使用
sleep
脚本?
为了完整性,其中一个测试脚本如下所示:
#!/bin/sh
echo "starting up server 1(parent)..."
$JAVA siena.StartServer -port 7000 &
server1=$!
sleep 2
echo "starting up server 2(the first child)..."
$JAVA siena.StartServer -port 2000 -master senp://:7000 > outputs/test.out2 &
server2=$!
echo "starting up server 3(the second child)..."
$JAVA siena.StartServer -port 3000 -master senp://:7000 > outputs/test.out2 &
server3=$!
sleep 5
kill $server1
kill $server2
kill $server3
答案1
您必须等待进程终止。通常,等待 SIGTERM 和 SIGKILL 之间是一个好主意。
在没有请求正常关闭之前就直接向进程发送 SIGKILL 指令,这被认为是不礼貌的行为(在我看来,这相当不专业)。这不仅会造成不一致,还会隐藏错误的关闭程序(程序无法在要求的时间内关闭)。
我会使用类似的东西:
# Launch your processes...
DEADLINE=1 #seconds to wait
kill $server1 $server2 $server3
sleep $DEADLINE
if kill -KILL $server1 $server2 $server3
then
echo "Warning: at least one process had not finished in $DEADLINE seconds" >&2
sleep 1 ## Wait for these just-killed processes to actually die and free their ports
fi
另一种方法是从服务器本身内部找到一个空闲端口(不绑定端口并以某种方式在分配的端口外进行通信)。这将允许并行测试。您可以将端口号写入命令行中指定的文件(而不是端口,您将传递文件名),从而更改-port 7000
为类似于-port auto -port-file test-port.txt
然后等待此文件出现然后-master senp://:$(cat test-port.txt)
。
另一个想法是根据 的信息自动找出netstat -ntl
所需端口是否空闲。然后,您可以选择等待或指定一组新的可用端口。在您看到端口空闲和实际绑定之间可能会发生竞争条件,特别是如果您的分配算法是确定性和可重复的(例如使用从 40000 开始的第一个可用端口)。让操作系统自己选择一个空闲端口要可靠得多。