我确实在 Bash 脚本中启动了一个长时间运行的后台进程。将进程发送到后台后,我将 PID 号保存在变量中,并在必要时使用该 PID 号来终止该进程。
但是,如果该后台进程在我的脚本杀死它之前以某种方式终止,并且系统为新创建的进程分配相同的 PID 号,那么当我使用该数字杀死该后台进程时,此操作可能会杀死该新创建的进程(取决于权限) , 当然)。
我知道,使用过的 PID 号不会在短时间内分配给任何新创建的进程,但我的脚本运行了数周,所以这是可能的。
怎样才能避免这样的事故发生呢?
我将 PID 与 /proc/<PID> 的时间戳组合成一个 uniq id,而不用担心杀死错误的 PID。
保存$PID:
echo $PID $(stat --format %Z /proc/$PID/comm) > pid
Run Code Online (Sandbox Code Playgroud)
安全地杀死 $PID:
read PID TIMESTAMP < pid
[[ $(stat --format %Z /proc/$PID/comm) != $TIMESTAMP ]] || kill -SIGKILL $PID
Run Code Online (Sandbox Code Playgroud)
这样,即使$PID被另一个进程回收,它的创建时间(/proc/$PID/comm的时间戳)也会不同,因此不可能重复。
PS:这[[ ... ]] || cmd
意味着如果[[ ]]
为真则不执行任何操作,否则运行cmd
。
编辑:我们确实有其他许多解决方法,但我想为什么不直接解决它呢?这应该是最简单的直接方式,不需要后台服务,不需要高级系统库,例如大多数容器都没有很好支持的systemd。我在一个需要中断其他正在进行的进程的产品中使用了这种方式。
EDIT2:当你杀死你的后台进程时,你最好在一个单独的进程组中启动你的后台进程,例如setsid background_process &
,然后在background_process的任何子进程中,你可以通过获取进程组id ps -o pgid= $$
,然后在killer端杀死进程组ID,然后所有子进程将被原子地杀死。否则,你杀死一个正常的进程ID,那么它的子进程仍然活着,即使使用pkill -P parant_pid
它仍然有机会让新的子进程逃脱。