向进程组的所有成员发送信号的最佳方式是什么?

新手上路,请多包涵

我想杀死整个进程树。使用任何常见的脚本语言来做到这一点的最佳方法是什么?我正在寻找一个简单的解决方案。

原文由 Adam Peck 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 775
2 个回答

您没有说要杀死的树是否是单个进程组。 (如果树是从服务器启动或 shell 命令行分叉的结果,通常会出现这种情况。)您可以使用 GNU ps 发现进程组,如下所示:

  ps x -o  "%p %r %y %x %c "

如果它是您要杀死的进程组,只需使用 kill(1) 命令,但不要给它一个进程号,而是给它组号的 _否定_。例如要杀死组 5112 中的每个进程,请使用 kill -TERM -- -5112

原文由 Norman Ramsey 发布,翻译遵循 CC BY-SA 3.0 许可协议

使用 进程组 ID ( PGID ) 杀死属于同一 进程树 的所有进程

  • kill -- -$PGID 使用默认信号( TERM = 15)
  • kill -9 -$PGID 使用信号 KILL (9)

您可以从同一 进程树 的任何 进程 ID ( PID ) 检索 PGID

  • kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*') (信号 TERM
  • kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*') (信号 KILL

特别感谢 tanagerSpeakus$PID 剩余空间和 OSX 兼容性的贡献。

解释

  • kill -9 -"$PGID" => 向所有子孙发送信号 9 ( KILL )…
  • PGID=$(ps opgid= "$PID") => 从树的任何 Process-ID 中检索 Process-Group-ID ,而不仅仅是 Process-Parent-IDps opgid= $PID 的变体是 ps -o pgid --no-headers $PID 其中 pgid 可以替换为 pgrp

但: - psPID 小于五位数并且右对齐时插入前导空格,如 tanager 所示。您可以使用:

**`PGID=$(ps opgid= "$PID" | tr -d ' ')`**
  • ps 从 OSX 总是打印标题,因此 Speakus 建议:

    PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"

  • grep -o [0-9]* 仅打印连续数字(不打印空格或字母标题)。

更多命令行

PGID=$(ps -o pgid= $PID | grep -o [0-9]*)
kill -TERM -"$PGID"  # kill -15
kill -INT  -"$PGID"  # correspond to [CRTL+C] from keyboard
kill -QUIT -"$PGID"  # correspond to [CRTL+\] from keyboard
kill -CONT -"$PGID"  # restart a stopped process (above signals do not kill it)
sleep 2              # wait terminate process (more time if required)
kill -KILL -"$PGID"  # kill -9 if it does not intercept signals (or buggy)

局限性

  • 正如 davideHubert Kario 所注意到的,当属于同一棵树的进程调用 kill 时, kill 有在终止整个树杀戮之前杀死自己的风险。
  • 因此,请务必使用具有不同 Process-Group-ID 的进程运行命令。

很长的故事

> cat run-many-processes.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./child.sh background &
./child.sh foreground
echo "ProcessID=$$ ends ($0)"

> cat child.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./grandchild.sh background &
./grandchild.sh foreground
echo "ProcessID=$$ ends ($0)"

> cat grandchild.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
sleep 9999
echo "ProcessID=$$ ends ($0)"

使用“&”在后台运行进程树

> ./run-many-processes.sh &
ProcessID=28957 begins (./run-many-processes.sh)
ProcessID=28959 begins (./child.sh)
ProcessID=28958 begins (./child.sh)
ProcessID=28960 begins (./grandchild.sh)
ProcessID=28961 begins (./grandchild.sh)
ProcessID=28962 begins (./grandchild.sh)
ProcessID=28963 begins (./grandchild.sh)

> PID=$!                    # get the Parent Process ID
> PGID=$(ps opgid= "$PID")  # get the Process Group ID

> ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
28348 28349 28349 28349 pts/3    28969 Ss   33021   0:00 -bash
28349 28957 28957 28349 pts/3    28969 S    33021   0:00  _ /bin/sh ./run-many-processes.sh
28957 28958 28957 28349 pts/3    28969 S    33021   0:00  |   _ /bin/sh ./child.sh background
28958 28961 28957 28349 pts/3    28969 S    33021   0:00  |   |   _ /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3    28969 S    33021   0:00  |   |   |   _ sleep 9999
28958 28963 28957 28349 pts/3    28969 S    33021   0:00  |   |   _ /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3    28969 S    33021   0:00  |   |       _ sleep 9999
28957 28959 28957 28349 pts/3    28969 S    33021   0:00  |   _ /bin/sh ./child.sh foreground
28959 28960 28957 28349 pts/3    28969 S    33021   0:00  |       _ /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3    28969 S    33021   0:00  |       |   _ sleep 9999
28959 28962 28957 28349 pts/3    28969 S    33021   0:00  |       _ /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3    28969 S    33021   0:00  |           _ sleep 9999
28349 28969 28969 28349 pts/3    28969 R+   33021   0:00  _ ps fj

命令 pkill -P $PID 不会杀死孙子:

 > pkill -P "$PID"
./run-many-processes.sh: line 4: 28958 Terminated              ./child.sh background
./run-many-processes.sh: line 4: 28959 Terminated              ./child.sh foreground
ProcessID=28957 ends (./run-many-processes.sh)
[1]+  Done                    ./run-many-processes.sh

> ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
28348 28349 28349 28349 pts/3    28987 Ss   33021   0:00 -bash
28349 28987 28987 28349 pts/3    28987 R+   33021   0:00  _ ps fj
    1 28963 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3    28987 S    33021   0:00  _ sleep 9999
    1 28962 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3    28987 S    33021   0:00  _ sleep 9999
    1 28961 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3    28987 S    33021   0:00  _ sleep 9999
    1 28960 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3    28987 S    33021   0:00  _ sleep 9999

命令 kill -- -$PGID 杀死所有进程,包括孙子进程。

 > kill --    -"$PGID"  # default signal is TERM (kill -15)
> kill -CONT -"$PGID"  # awake stopped processes
> kill -KILL -"$PGID"  # kill -9 to be sure

> ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
28348 28349 28349 28349 pts/3    29039 Ss   33021   0:00 -bash
28349 29039 29039 28349 pts/3    29039 R+   33021   0:00  _ ps fj

结论

我注意到在这个例子中 PIDPGID 是相等的( 28957 )。

这就是为什么我最初认为 kill -- -$PID 就足够了。但如果进程在 Makefile 中产生,则 Process IDGroup ID 不同。

我认为 kill -- -$(ps -o pgid= $PID | grep -o [0-9]*) 是从不同的 组 ID (另一个进程树)调用时杀死整个进程树的最佳简单技巧。

原文由 oHo 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题