我有许多bash脚本正在执行许多相似的任务,它们使用一些外部二进制程序。问题在于这些二进制程序经常无法正常退出终止。由于我的脚本要执行它们数千次,这些闲置或几乎死亡的进程实例会迅速累积。我无法修复这些程序,因此需要确保我的bash脚本终止它们。
已经有一些涉及终止bash脚本进程的话题在SE中。我已经应用和测试了那些内容,而且在某种程度上它们确实可行。但是对于我的情况来说并不够好,我不明白为什么,因此我开了一个新的问题。
我的脚本具有层次结构,以下是简化后的示例: 脚本A调用脚本B,脚本B并行调用多个脚本C实例以使用所有CPU。例如,脚本B并行运行5个脚本C实例,并且当其中一个脚本C实例完成时,它将启动一个新的实例,总共运行数千次脚本C。脚本C调用多个外部二进制程序/命令,它们没有很好地终止。它们在后台并行运行并相互通信。
然而,我的脚本C能够检测到外部命令何时完成它们的工作,即使它们没有终止,然后我的bash脚本退出。
为了在bash脚本完成时终止所有外部程序,我添加了一个退出陷阱:
现在,如果我只同时运行很少数量的C脚本实例,似乎这个方法可以工作。但是,如果我增加数量到更多,例如10个(工作站很强大,应该能够处理数十个并行的C脚本实例和外部程序),那么它就不能再工作了,并且数百个外部程序实例会迅速累积。
但我不明白为什么。例如,其中一个积累的进程PID是32048。在日志中,我可以看到执行退出陷阱:
已经有一些涉及终止bash脚本进程的话题在SE中。我已经应用和测试了那些内容,而且在某种程度上它们确实可行。但是对于我的情况来说并不够好,我不明白为什么,因此我开了一个新的问题。
我的脚本具有层次结构,以下是简化后的示例: 脚本A调用脚本B,脚本B并行调用多个脚本C实例以使用所有CPU。例如,脚本B并行运行5个脚本C实例,并且当其中一个脚本C实例完成时,它将启动一个新的实例,总共运行数千次脚本C。脚本C调用多个外部二进制程序/命令,它们没有很好地终止。它们在后台并行运行并相互通信。
然而,我的脚本C能够检测到外部命令何时完成它们的工作,即使它们没有终止,然后我的bash脚本退出。
为了在bash脚本完成时终止所有外部程序,我添加了一个退出陷阱:
# Exit cleanup
cleanup_exit() {
# Running the termination in an own process group to prevent it from preliminary termination. Since it will run in the background it will not cause any delays
setsid nohup bash -c "
touch /tmp/trace_1 # To see if this code was really executed to this point
# Trapping signals to prevent that this function is terminated preliminary
trap '' SIGINT SIGQUIT SIGTERM SIGHUP ERR
touch /tmp/trace_2 # To see if this code was really executed to this point
# Terminating the main processes
kill ${pids[@]} 1>/dev/null 2>&1 || true
touch /tmp/trace_3
sleep 5
touch /tmp/trace_4
kill -9 ${pids[@]} 1>/dev/null 2>&1 || true
touch /tmp/trace_5
# Terminating the child processes of the main processes
echo "Terminating the child processes"
pkill -P ${pids[@]} 1>/dev/null 2>&1 || true
touch /tmp/trace_6
sleep 1
pkill -9 -P ${pids[@]} 1>/dev/null 2>&1 || true
touch /tmp/trace_7
# Terminating everything else which is still running and which was started by this script
pkill -P $$ || true
touch /tmp/trace_8
sleep 1
pkill -9 -P $$ || true
touch /tmp/trace_9
"
}
trap "cleanup_exit" SIGINT SIGQUIT SIGTERM EXIT
现在,如果我只同时运行很少数量的C脚本实例,似乎这个方法可以工作。但是,如果我增加数量到更多,例如10个(工作站很强大,应该能够处理数十个并行的C脚本实例和外部程序),那么它就不能再工作了,并且数百个外部程序实例会迅速累积。
但我不明白为什么。例如,其中一个积累的进程PID是32048。在日志中,我可以看到执行退出陷阱:
+ echo ' * Snapshot 190 completed after 3 seconds.'
* Snapshot 190 completed after 3 seconds.
+ break
+ cleanup_exit
+ echo
+ echo ' * Cleaning up...'
* Cleaning up...
+ setsid nohup bash -c '
touch /tmp/trace_1 # To see if this code was really executed to this point
# Trapping signals to prevent that this function is terminated preliminary
trap '\'''\'' SIGINT SIGQUIT SIGTERM SIGHUP ERR
touch /tmp/trace_2 # To see if this code was really executed to this point
# Terminating the main processes
kill 31678' '32048 1>/dev/null 2>&1 || true
touch /tmp/trace_3
sleep 5
touch /tmp/trace_4
kill -9 31678' '32048 1>/dev/null 2>&1 || true
touch /tmp/trace_5
# Terminating the child processes of the main processes
pkill -P 31678' '32048 1>/dev/null 2>&1 || true
touch /tmp/trace_6
sleep 1
pkill -9 -P 31678' '32048 1>/dev/null 2>&1 || true
touch /tmp/trace_7
# Terminating everything else which is still running and which was started by this script
pkill -P 31623 || true
touch /tmp/trace_8
sleep 1
pkill -9 -P 31623 || true
touch /tmp/trace_9
'
显然,该进程的PID被用于退出陷阱,但是进程没有退出。为了测试,我手动再次对这个进程运行了kill命令,然后它确实退出了。
最有趣的是,只有前5个跟踪文件出现了。为什么没有超过5个呢?
更新:我刚刚发现即使我并行运行脚本C的一个实例,即顺序运行,它也只能在一段时间内正常工作。突然在某个时间点,进程不再被终止,而是开始永久地挂在那里并积累。机器不应该被一个并行进程过载。在我的日志文件中,退出陷阱仍然像以前一样被正确调用,没有任何区别。内存也是空闲的,CPU也部分空闲。