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
kill
is invoked by a process belonging to the same tree, kill
risks to kill itself before terminating the whole tree killing. 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/49668.html