gpt4 book ai didi

linux - 杀死由 Bash 脚本启动的所有进程

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:26:26 28 4
gpt4 key购买 nike

我有许多执行许多类似任务的 bash 脚本,它们使用一些外部二进制程序。问题是二进制程序通常不会像它们应该的那样退出终止。由于我的脚本运行了数千次,因此这些进程的大量闲置/几乎死亡的实例很快就会累积起来。我无法修复这些程序,因此我需要确保我的 bash 脚本正在终止它们。

SE 中已经有一些主题处理终止 bash 脚本进程的任务。我已经应用并测试了那里写的东西,并且在某种程度上它是有效的。但它对我的情况来说效果不够好,我不明白为什么,因此我提出了一个新问题。

我的脚本有一个层次结构,这里以简化的方式显示:脚本 A 调用脚本 B,脚本 B 并行调用脚本 C 的多个实例以使用所有 CPU。例如。脚本 B 并行运行脚本 C 的 5 个实例,当脚本 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
'

很明显,exit trap中使用了这个进程的PID,但是进程并没有退出。为了测试,我在此进程上再次手动运行 kill 命令,然后它确实退出了。

最有趣的是,只出现了直到编号 5 的跟踪文件。没有超过 5,但为什么呢?

更新:我刚刚发现,即使我只并行运行脚本 C 的一个实例,即顺序运行,它也只能在一段时间内运行良好。突然在某个时间点进程不再终止,而是开始永远徘徊并积累。机器不应因并行的一个过程而重载。在我的日志文件中,退出陷阱仍然像以前一样正确调用,没有区别。内存也是免费的,CPU 也是部分免费的。

最佳答案

对任何 shell 脚本进行健全性检查的一个很好的方法是在其上运行 ShellCheck:

Line 9:
kill ${pids[@]} 1>/dev/null 2>&1 || true
^-- SC2145: Argument mixes string and array. Use * or separate argument.

确实,您的 xtrace 在这一行做了一些奇怪的事情:

kill 31678' '32048 1>/dev/null 2>&1 || true
^^^--- What is this?

这里的问题是你的 ${pids[@]} 扩展成多个单词,而 bash -c 只解释第一个单词。这是一个简化的示例:

pids=(2 3 4)
bash -c "echo killing ${pids[@]}"

这最终写成killing 2,没有提到3或4。相当于运行

bash -c "echo killing 2" "3" "4" 

其他 pid 只是成为位置参数 $0$1 而不是已执行命令的一部分。

相反,喜欢 ShellCheck suggested ,您希望 * 将所有 pids 与空格连接起来并将它们作为单个参数插入:

pids=(2 3 4)
bash -c "echo killing ${pids[*]}"

打印killing 2 3 4

关于linux - 杀死由 Bash 脚本启动的所有进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46513568/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com