![如何将 shell 命令与在没有竞争条件的情况下退出的非子进程链接起来?](https://linux22.com/image/112739/%E5%A6%82%E4%BD%95%E5%B0%86%20shell%20%E5%91%BD%E4%BB%A4%E4%B8%8E%E5%9C%A8%E6%B2%A1%E6%9C%89%E7%AB%9E%E4%BA%89%E6%9D%A1%E4%BB%B6%E7%9A%84%E6%83%85%E5%86%B5%E4%B8%8B%E9%80%80%E5%87%BA%E7%9A%84%E9%9D%9E%E5%AD%90%E8%BF%9B%E7%A8%8B%E9%93%BE%E6%8E%A5%E8%B5%B7%E6%9D%A5%EF%BC%9F.png)
有时,我意识到当一个进程完成时我需要运行另一个命令。如果它在同一个 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 保持未使用)cd
,cd
则将失败并且子 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 吗?另一方面,如果锁定文件消失了,那么您的进程就完成了?