¿Cuál es la mejor manera de enviar una señal a todos los miembros de un grupo de proceso?

Resuelto Adam Peck asked hace 15 años • 35 respuestas

Quiero matar un árbol de procesos completo. ¿Cuál es la mejor manera de hacer esto utilizando lenguajes de secuencias de comandos comunes? Estoy buscando una solución sencilla.

Adam Peck avatar Dec 25 '08 01:12 Adam Peck
Aceptado

No dices si el árbol que quieres eliminar es un grupo de proceso único. (Este suele ser el caso si el árbol es el resultado de una bifurcación desde el inicio de un servidor o una línea de comando de shell). Puede descubrir grupos de procesos usando GNU ps de la siguiente manera:

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

Si desea eliminar un grupo de procesos, simplemente use el kill(1)comando pero en lugar de darle un número de proceso, dale la negación del número de grupo. Por ejemplo, para finalizar todos los procesos del grupo 5112, utilice kill -TERM -- -5112.

Norman Ramsey avatar Dec 24 '2008 20:12 Norman Ramsey

Elimine todos los procesos que pertenecen al mismo árbol de procesos utilizando el ID del grupo de procesos ( PGID)

  • kill -- -$PGID     Usar señal predeterminada ( TERM= 15)
  • kill -9 -$PGID     Usa la señal KILL(9)

Puede recuperarlo PGIDde cualquier ID de proceso ( PID) del mismo árbol de procesos.

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

Un agradecimiento especial a tanager y Speakus por sus contribuciones sobre $PIDlos espacios restantes y la compatibilidad con OSX.

Explicación

  • kill -9 -"$PGID"=> Enviar señal 9 ( KILL) a todos los hijos y nietos...
  • PGID=$(ps opgid= "$PID")=> Recupere el ID de grupo de procesos de cualquier ID de proceso del árbol, no solo el ID de padre de proceso . Una variación de ps opgid= $PIDes ps -o pgid --no-headers $PIDdonde pgidse puede reemplazar por pgrp.
    Pero:
    • psinserta espacios iniciales cuando PIDtiene menos de cinco dígitos y está alineado a la derecha como lo notó tangara . Puedes usar:
      PGID=$(ps opgid= "$PID" | tr -d ' ')
    • psdesde OSX siempre imprime el encabezado, por eso Speakus propone:
      PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
  • grep -o [0-9]*imprime únicamente dígitos sucesivos (no imprime espacios ni encabezados alfabéticos).

Otras líneas de comando

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)

Limitación

  • Como lo notaron Davide y Hubert Kario , cuando killes invocado por un proceso que pertenece al mismo árbol, killcorre el riesgo de suicidarse antes de terminar con la destrucción del árbol.
  • Por lo tanto, asegúrese de ejecutar el comando utilizando un proceso que tenga un ID de grupo de proceso diferente .

Larga historia

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

Ejecute el árbol de procesos en segundo plano usando '&'

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

El comando pkill -P $PIDno mata al nieto:

> 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

El comando kill -- -$PGIDmata todos los procesos, incluido el nieto.

> 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

Conclusión

Noto en este ejemplo PIDy PGIDson iguales ( 28957).
Por eso al principio pensé kill -- -$PIDque era suficiente. Pero en el caso de que el proceso se genere dentro de un, el MakefileID del proceso es diferente del ID del grupo .

Creo que kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)es el mejor truco sencillo para eliminar un árbol de procesos completo cuando se llama desde un ID de grupo diferente (otro árbol de procesos).

oHo avatar Feb 28 '2013 15:02 oHo
pkill -TERM -P 27888

Esto eliminará todos los procesos que tengan el ID de proceso principal 27888.

O más robusto:

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

que programa la muerte 33 segundos después y solicita cortésmente que los procesos finalicen.

Consulte esta respuesta para eliminar a todos los descendientes.

Onlyjob avatar Jun 26 '2011 00:06 Onlyjob

Para eliminar un árbol de procesos de forma recursiva, utilice killtree():

#!/bin/bash

killtree() {
    local _pid=$1
    local _sig=${2:--TERM}
    kill -stop ${_pid} # needed to stop quickly forking parent from producing children between child killing and parent killing
    for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
        killtree ${_child} ${_sig}
    done
    kill -${_sig} ${_pid}
}

if [ $# -eq 0 -o $# -gt 2 ]; then
    echo "Usage: $(basename $0) <pid> [signal]"
    exit 1
fi

killtree $@
zhigang avatar Jul 09 '2010 08:07 zhigang