GNU/Linux >> Tutoriales Linux >  >> Linux

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

No dices si el árbol que quieres matar 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 se trata de un grupo de procesos que desea eliminar, simplemente use el kill(1) pero en lugar de darle un número de proceso, déle la negación del número de grupo. Por ejemplo, para eliminar todos los procesos del grupo 5112, use kill -TERM -- -5112 .


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

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

Puedes recuperar el PGID desde cualquier Process-ID (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 )

Explicación

  • kill -9 -"$PGID" => Enviar señal 9 (KILL ) a todos los hijos y nietos...
  • PGID=$(ps opgid= "$PID") => Recuperar el Process-Group-ID desde cualquier Process-ID del árbol, no solo el Process-Parent-ID . Una variación de ps opgid= $PID es ps -o pgid --no-headers $PID donde pgid puede ser reemplazado por pgrp .
    Pero:
    • ps inserta espacios iniciales cuando PID tiene menos de cinco dígitos y está alineado a la derecha como lo notó tangara. Puedes usar:
      PGID=$(ps opgid= "$PID" | tr -d ' ')
    • ps desde OSX siempre imprima el encabezado, por lo que Speakus propone:
      PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
  • grep -o [0-9]* imprime solo dígitos sucesivos (no imprime espacios ni encabezados alfabéticos).

Más 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 notaron davide y Hubert Kario, cuando kill es invocado por un proceso que pertenece al mismo árbol, kill corre el riesgo de suicidarse antes de terminar con la matanza de árboles.
  • Por lo tanto, asegúrese de ejecutar el comando usando un proceso que tenga un Process-Group-ID 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 $PID no 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 -- -$PGID mata 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

Observo en este ejemplo PID y PGID son iguales (28957 ).
Es por eso que originalmente pensé kill -- -$PID fue suficiente. Pero en el caso de que el proceso se genere dentro de un Makefile el ID del proceso es diferente del ID de grupo .

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


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 matar 33 segundos más tarde y pide cortésmente que finalicen los procesos.

Vea esta respuesta para terminar con todos los descendientes.


Linux
  1. Qué proceso está usando todo mi disco IO

  2. Linux:averigüe qué proceso está utilizando toda la RAM?

  3. Acabo de eliminar /bin. ¿Cuál es la mejor manera de recuperarse?

  4. ¿Cuándo envía el sistema un SIGTERM a un proceso?

  5. ¿Cuál es la forma más rápida de eliminar todos los archivos y subcarpetas de un directorio?

Cómo enumerar los miembros de un grupo en Linux

SIGTERM vs SIGKILL:¿Cuál es la diferencia?

¿Qué es el comando matar en Linux?

¿Qué es un proceso detenido en Linux?

¿Cuál es la mejor manera de establecer una variable de entorno en .bashrc?

¿Cuál es la mejor manera de verificar si un volumen está montado en un script Bash?