如何将 shell 命令与在没有竞争条件的情况下退出的非子进程链接起来?

如何将 shell 命令与在没有竞争条件的情况下退出的非子进程链接起来?

有时,我意识到当一个进程完成时我需要运行另一个命令。如果它在同一个 shell 中(并且我可以控制 -Z 它),那么我可以以某种方式将“&& prog2”添加到已经运行的 prog1 中吗?提供了很多好的解决方案。然而,那些仅当它是子进程时才有效

有一个相当标准的习惯用法,至少对于共享相同用户 ID 的进程来说是这样,在 shell 中如下所示:

while kill -s 0 $pid 2> /dev/null; do sleep 1; done

在系统/proc(如 Linux)上,您可以使用[ -d /proc/$pid ]代替kill来免除相同的用户 ID 要求。

但这两者都有竞争条件:进程可能会在 期间退出sleep,然后新进程可以获得相同的 PID。因此,循环将继续下去,而不会意识到它感兴趣的进程实际上已退出。

有没有办法消除 shell 脚本中的竞争条件?

答案1

可能是 Linux 特定的,其他的/proc行为可能有所不同,但在 Linux 上你可以这样做:

(   # subshell to preserve CWD
    cd /proc/$pid || exit
    [ "$expected_path" = "$(readlink exe)" ] || exit # optional, and can do more checks
    while [ -d . ]; do sleep 1; done
)

.当进程退出时,工作目录将不再有效(并且将不再存在)。然而,如果一个新进程采用该 pid,它就会不是再次生效。如果进程在 之前退出(并且 pid 保持未使用)cdcd则将失败并且子 shell 退出。如果 pid 在 之前已被重新使用cd,那么将会成功,但“可选”检查(感谢 LJKims)有望捕获它。因此,只要检查能够捕获非常短的可能竞争,就不存在竞争条件。要进行测试,您可以使用以下内容:

# shell 1
$ sleep 10 &
[1] 26453
$ cd /proc/26453

# shell 2 (must be bash for BASHPID)
for ((i=0; i<200000; ++i)) do ( if [ 26453 -eq $BASHPID ]; then echo "I am have the pid — sleeping 60"; sleep 60; echo "done sleeping"; fi ); done
I have the pid — sleeping 60

# back to shell 1
[ -d . ]; echo $?
1

因此,尽管新的 pid 为 26453,但它仍然注意到它感兴趣的睡眠已退出。

答案2

假设您可以停止该过程(例如,通过您提到的ctrl+ z),这是一种非竞争性的模拟方法&& prog2(实际上,更准确地说; prog2,因为不会检查退出状态,但可以添加)。

找到PID。打开另一个终端并执行

strace -f -e trace=exit_group -p $PID && prog2

然后,恢复该过程。

答案3

想必您不想仅根据进程的 PID 来监视进程。也就是说,您可能知道这个过程是什么。为什么不监视 /proc/<pid>/cmdline 的内容,一旦它不再存在或不再符合您的期望,您就可以执行您的命令/作业?

答案4

也许锁定文件是一个好方法。您甚至可以嵌入传递给子进程的字符串并将其放入锁定文件/锁定文件名的一部分中。

锁定文件可以作为进程实际结束的一部分被删除。然后,您可以按名称或内容查找锁定文件,并将 PID 放入锁定文件中。如果锁定文件在那里,它仍在运行,您可以从文件中获取 PID 吗?另一方面,如果锁定文件消失了,那么您的进程就完成了?

相关内容