Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 446
Adam Peck Avatar asked Dec 24 '08 18:12

Adam Peck


People also ask

How do you send a signal to process a group?

The killpg() function sends a signal to a process group. A process has permission to send a signal if the real or effective user ID of the sender is the same as the real or effective user ID of the intended recipient. A process can also send signals if it has appropriate privileges.

Which command is used to send a signal to multiple processes based on name?

Pkill Command – Send signal to the process based on its name. You can send signal to any process by specifying the full name or partial name.

Which command allows you to send a signal of your choice to a process?

Kill is used to send a signal to a process. The most commonly used signal is "terminate" (SIGTERM) or "kill" (SIGKILL).

Can a process send a signal to another process?

If the process is privileged, send the signal to all processes except for some special system processes. Otherwise, send the signal to all processes with the same effective user ID. A process can send a signal to itself with a call like kill (getpid(), signum ) .


2 Answers

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.

like image 172
Norman Ramsey Avatar answered Sep 23 '22 09:09

Norman Ramsey


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

like image 27
oHo Avatar answered Sep 24 '22 09:09

oHo