繁体   English   中英

Bash `wait` 命令,等待超过 1 个 PID 完成执行

[英]Bash `wait` command, waiting for more than 1 PID to finish execution

我最近发布了一个问题,询问是否可以防止重复使用 PID

到目前为止,答案似乎是否定的。 (这很好。)

但是,用户Diego Torres Milano为该问题添加了一个答案,我在这里的问题与该答案有关。

迭戈回答说

如果您害怕重复使用 PID,如果您等待其他答案的解释就不会发生这种情况,您可以使用

echo 4194303 > /proc/sys/kernel/pid_max

减少你的恐惧;-)

我实际上不明白为什么迭戈在这里使用了数字4194303 ,但这是另一个问题。

我的理解是我遇到了以下代码的问题:

for pid in "${PIDS[@]}"
do
    wait $pid
done

问题是我在一个数组中有多个 PID,并且 for 循环将与数组中的每个 PID 按顺序运行wait命令,但是我无法预测进程将按照其 PID 存储在此的相同顺序完成大批。

IE; 可能会发生以下情况:

  • 开始等待数组索引 0 中的 PID
  • PID 在数组索引 1 中的进程终止
  • 新作业在系统上运行,导致存储在 PID 数组索引 1 中的 PID 被另一个进程重用
  • wait终止,因为数组索引 0 中的 PID 退出
  • 开始在数组索引 0 中等待 PID,除了现在这是一个不同的过程,我们不知道它是什么
  • 重新使用wait当前等待的 PID 的进程永远不会终止。 也许它是邮件服务器的 PID 或系统管理员已启动的东西。
  • wait一直等到发现下一个严重的 linux 错误并且系统重新启动或停电

迭戈说:

如果您等待其他答案的解释,这将不会发生

IE; 我上面描述的情况不会发生。

迭戈正确吗?

  • 如果是这样,为什么我上面描述的情况不会发生?

还是迭戈不正确?

  • 如果是这样,那么我今天晚些时候发布一个新问题......

补充说明

我发现这个问题可能会令人困惑,除非您知道 PID 是在后台启动的进程的 PID。 IE;

my_function &
PID="$!"
PIDS+=($PID)

让我们来看看你的选择。

无条件等待所有后台作业

for i in 1 2 3 4 5; do
    cmd &
done
wait

这样做的好处是简单,但你不能让你的机器忙碌。 如果你想在旧工作完成后开始新工作,你不能。 在所有后台作业完成之前,您的机器利用率越来越低,此时您可以开始新的一批作业。

相关的是通过将多个参数传递给waitwait作业子集的能力:

unrelated_job &
for i in 1 2 3 4 5; do
  cmd & pids+=($!)
done
wait "${pids[@]}"   # Does not wait for unrelated_job, though

以任意顺序等待单个作业

for i in 1 2 3 4 5; do
   cmd & pids+=($!)
done

for pid in "${pids[@]}"; do
   wait "$pid"
   # do something when a job completes
done

这有让你在作业完成后做工作的好处,但仍然有比其他工作问题$pid可能会先完成,让你的机器得到充分利用,直到$pid实际完成。 但是,您仍然可以获得每个单独作业的退出状态,即使它在您实际等待之前完成。

等待下一个作业完成( bash 4.3 或更高版本)

for i in 1 2 3 4 5; do
   cmd & pids+=($!)
done

for pid in "${pids[@]}"; do
   wait -n
   # do something when a job completes
done

在这里,你可以等到工作完成,这意味着你可以让你的机器尽可能忙。 唯一的问题是,您不一定知道完成了哪个作业,而无需使用jobs来获取活动进程列表并将其与pids进行比较。

其他选择?

shell 本身并不是进行作业分配的理想平台,这就是为什么有许多程序设计用于管理批处理作业: xargsparallelslurmqsub等。

这是旧的,但是由于pid冲突而延迟wait等待一些随机无关进程的情况尚未直接解决。

这在内核级别是不可能的。 它的工作方式是在父进程调用wait(2) ¹ 之前,子进程仍然存在 因为孩子仍然存在,linux 将耗尽 pids 而不是重用它。 这有时会通过所谓的僵尸或“失效”进程表现出来 - 这些是已经退出但尚未被其父进程“收割”的子进程。

现在,在 shell 级别,您不必为要收割的子进程调用wait(1) ¹ - bash会自动执行此操作。 我还没有确认,但是当你为一个很久以前退出的孩子 pid 运行wait $pid ,我敢打赌bash意识到它已经收获了那个孩子并立即返回信息而不是等待任何东西。

¹ wait(N)表示法是用于消除 API 层之间歧义的约定 - N 指的是命令/函数所在的手册部分。在这种情况下,我们有:

  • wait(2) :系统调用 - 参见man 2 wait
  • wait(1) :shell 命令 - 参见man 1 waithelp wait

如果您想知道每个手册部分中的内容,请尝试man N intro

Bash 5.1开始,由于引入了wait -p ,现在有一种处理多个后台作业的额外方法。

下面是一个例子:

#!/usr/bin/env bash
for ((i=0; i < 10; i++)); do
    secs=$((RANDOM % 10)); code=$((RANDOM % 256))
    (sleep ${secs}; exit ${code}) &
    echo "Started background job (pid: $!, sleep: ${secs}, code: ${code})"
done

while true; do
    wait -n -p pid; code=$?
    [[ -z "${pid}" ]] && break
    echo "Background job ${pid} finished with code ${code}"
done

这里的新奇之处在于,您现在确切地知道完成了哪一项后台作业。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM