What's the best way to send a signal to all members of a process group?

I want to kill a whole process tree. What is the best way to do this using any common scripting languages? I am looking for a simple solution.


You don't say if the tree you want to kill is a single process group. (This is often the case if the tree is the result of forking from a server start or a shell command line.) You can discover process groups using GNU ps as follows:

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

If it is a process group you want to kill, just use the kill(1) command but instead of giving it a process number, give it the negation of the group number. For example to kill every process in group 5112, use kill -TERM -- -5112 .


Kill all the processes belonging to the same process tree using the Process Group ID ( PGID )

  • kill -- -$PGID Use default signal ( TERM = 15)
  • kill -9 -$PGID Use the signal KILL (9)
  • You can retrieve the PGID from any Process-ID ( PID ) of the same process tree

  • kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*') (signal TERM )
  • kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*') (signal KILL )
  • Special thanks to tanager and Speakus for contributions on $PID remaining spaces and OSX compatibility.

    Explanation

  • kill -9 -"$PGID" => Send signal 9 ( KILL ) to all child and grandchild...
  • PGID=$(ps opgid= "$PID") => Retrieve the Process-Group-ID from any Process-ID of the tree, not only the Process-Parent-ID. A variation of ps opgid= $PID is ps -o pgid --no-headers $PID where pgid can be replaced by pgrp .
    But:
  • ps inserts leading spaces when PID is less than five digits and right aligned as noticed by tanager. You can use:
    PGID=$(ps opgid= "$PID" | tr -d ' ')
  • ps from OSX always print the header, therefore Speakus proposes:
    PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
  • grep -o [0-9]* prints successive digits only (does not print spaces or alphabetical headers).
  • Further command lines

    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)
    

    Limitation

  • As noticed by davide and Hubert Kario, when kill is invoked by a process belonging to the same tree, kill risks to kill itself before terminating the whole tree killing.
  • Therefore, be sure to run the command using a process having a different Process-Group-ID.

  • Long story

    > 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 the process tree in background using '&'

    > ./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
    

    The command pkill -P $PID does not kill the grandchild:

    > 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
    

    The command kill -- -$PGID kills all processes including the grandchild.

    > 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
    

    Conclusion

    I notice in this example PID and PGID are equal ( 28957 ).
    This is why I originally thought kill -- -$PID was enough. But in the case the process is spawn within a Makefile the Process ID is different from the Group ID.

    I think kill -- -$(ps -o pgid= $PID | grep -o [0-9]*) is the best simple trick to kill a whole process tree when called from a different Group ID (another process tree).


    pkill -TERM -P 27888
    

    This will kill all processes that have the parent process ID 27888.

    Or more robust:

    CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS
    

    which schedule killing 33 second later and politely ask processes to terminate.

    See this answer for terminating all descendants.

    链接地址: http://www.djcxy.com/p/86026.html

    上一篇: 如何使gdb可重复获取堆栈跟踪?

    下一篇: 向过程组的所有成员发送信号的最佳方式是什么?