458 votes

Quelle est la meilleure façon d'envoyer un signal à tous les membres d'un groupe de processus ?

Je veux tuer un arbre de processus entier. Quelle est la meilleure façon de le faire en utilisant un langage de script courant ? Je cherche une solution simple.

4 votes

Les zombies devraient disparaître quand le système de faucheur fonctionne. J'admets que j'ai vu des systèmes où les zombies s'attardent, mais c'est atypique.

10 votes

Parfois, ces zombies persistants sont responsables d'une activité effrayante.

0 votes

Utilisez l'un des chronos o herodes des commandes.

333voto

Norman Ramsey Points 115730

Vous ne dites pas si l'arbre que vous voulez tuer est un groupe de processus unique. (C'est souvent le cas si l'arbre est le résultat d'une bifurcation à partir du démarrage d'un serveur ou d'une ligne de commande shell). Vous pouvez découvrir les groupes de processus en utilisant GNU ps comme suit :

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

Si c'est un groupe de processus que vous voulez tuer, il suffit d'utiliser la commande kill(1) mais au lieu de lui donner un numéro de processus, donnez-lui la commande négation du numéro de groupe. Par exemple, pour tuer tous les processus du groupe 5112, utilisez kill -TERM -- -5112 .

4 votes

Kill -74313 -bash : kill : 74313 : invalid signal specification Si j'ajoute le kill -15 -GPID, cela fonctionne parfaitement.

57 votes

Comme pour la plupart des commandes, si vous voulez qu'un argument normal qui commence par un - ne soit pas interprété comme un interrupteur, faites-le précéder de -- : kill -- -GPID

11 votes

pgrep peut offrir un moyen plus facile de trouver l'ID du groupe de processus. Par exemple, pour tuer le groupe de processus de my-script.sh, exécutez kill -TERM -$(pgrep -o my-script.sh) .

227voto

olibre Points 6069

Tuer tous les processus appartenant au même arbre des processus en utilisant le ID du groupe de processus ( PGID )

  • kill -- -$PGID     Utiliser le signal par défaut ( TERM = 15)
  • kill -9 -$PGID     Utilisez le signal KILL (9)

Vous pouvez récupérer le PGID de n'importe quel Process-ID ( PID ) de la même arbre des processus

  • *`kill -- -$(ps -o pgid= $PID | grep -o '[0-9]')**   (signalTERM` )
  • *`kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]')**   (signalKILL` )

Remerciements particuliers à Tangara y Speakus pour des contributions sur $PID espaces restants et compatibilité OSX.

Explication

  • kill -9 -"$PGID" => Envoyer le signal 9 ( KILL ) à tous les enfants et petits-enfants...
  • PGID=$(ps opgid= "$PID") => Récupérer le Process-Group-ID de n'importe quel Process-ID de l'arbre, pas seulement le Process-Parent-ID . Une variation de ps opgid= $PID es ps -o pgid --no-headers $PID donde pgid peut être remplacé par pgrp .
    Mais :
    • ps insère des espaces en tête lorsque PID est inférieur à cinq chiffres et aligné à droite, comme le remarque l'inspecteur de la Commission européenne. Tangara . Vous pouvez utiliser :
      PGID=$(ps opgid= "$PID" | tr -d ' ')
    • ps d'OSX impriment toujours l'en-tête, par conséquent Speakus propose :
      PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
  • *`grep -o [0-9]`** imprime uniquement les chiffres successifs (n'imprime pas les espaces ni les en-têtes alphabétiques).

Autres lignes de commande

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

  • Comme l'a remarqué davide y Hubert Kario quand kill est invoqué par un processus appartenant au même arbre, kill risque de se tuer lui-même avant de mettre fin à l'abattage de l'arbre entier.
  • Par conséquent, assurez-vous d'exécuter la commande à l'aide d'un processus ayant un autre nom d'utilisateur. Process-Group-ID .

Longue histoire

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

Exécuter l'arbre des processus en arrière-plan en utilisant '&'.

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

La commande pkill -P $PID ne tue pas le petit-enfant :

> 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

La commande kill -- -$PGID tue tous les processus, y compris le petit-fils.

> 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

Je remarque dans cet exemple PID y PGID sont égales ( 28957 ).
C'est pourquoi j'ai d'abord pensé kill -- -$PID était suffisante. Mais dans le cas où le processus est frayé dans une Makefile el ID du processus est différente de la ID du groupe .

Creo que *`kill -- -$(ps -o pgid= $PID | grep -o [0-9])`** est la meilleure astuce pour tuer un processus entier lorsqu'il est appelé à partir d'un autre processus. ID du groupe (un autre arbre de processus).

0 votes

Si kill est invoqué par un processus appartenant au même arbre, fait kill risque de se tuer avant en terminant l'arbre entier ?

1 votes

Bonjour @davide. C'est une bonne question. Je pense que kill doit toujours envoyer le signal à l'ensemble de l'arbre avant de recevoir son propre signal. Mais dans certaines circonstances/implémentations spécifiques, kill peut s'envoyer le signal, être interrompu, puis recevoir son propre signal. Cependant, le risque devrait être assez minime, et peut être ignoré dans la plupart des cas, car d'autres bugs devraient se produire avant celui-ci. Ce risque peut-il être ignoré dans votre cas ? De plus, d'autres réponses ont ce bug commun ( kill de l'arbre des processus en cours de destruction). J'espère que cela vous aidera Merci ;)

3 votes

Cela ne fonctionne que si les sous-commandes elles-mêmes ne deviennent pas des chefs de groupe. Même des outils aussi simples que man le faire. D'un autre côté, si vous voulez tuer le processus gandchild depuis le processus child, kill -- -$pid ne fonctionnera pas. Ce n'est donc pas une solution générique.

173voto

Onlyjob Points 1244
pkill -TERM -P 27888

Cela va tuer tous les processus qui ont l'ID du processus parent 27888.

Ou plus robuste :

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

qui programment la mise à mort 33 secondes plus tard et demandent poliment aux processus de se terminer.

Voir cette réponse pour mettre fin à tous les descendants.

23 votes

Dans mon test rapide, pgrep n'a signalé que les enfants immédiats, donc il se peut que cela ne tue pas la hiérarchie entière.

10 votes

Je suis d'accord avec @haridsv : pkill -P envoie le signal à l'enfant seulement => le petit-enfant ne reçoit pas le signal => Par conséquent, j'ai mal agi. une autre réponse pour expliquer cela. A la vôtre ;-)

1 votes

A partir d'un script bash, pour tuer vos propres enfants, utilisez pkill -TERM -P ${$} .

106voto

zhigang Points 899

Pour tuer un arbre de processus de manière récursive, utilisez 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 $@

3 votes

El -- arguments pour ps ne fonctionnent pas sous OS X. Pour que cela fonctionne, il faut remplacer le ps commande par : ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g donde _regex est défini avant le for boucle : local _regex="[ ]*([0-9]+)[ ]+${_pid}"

6 votes

Les processus stoppés ne sont pas tués par SIGTERM. Voir mon réponse

1 votes

-1 utilise #!/bin/bash au lieu de #!/usr/bin/env bash (ou mieux encore les constructions POSIX seulement et /bin/sh)

10voto

Kim Stebel Points 22873

La réponse de brad est ce que je recommanderais également, sauf que vous pouvez vous passer de awk si vous utilisez l'option --ppid option pour ps .

for child in $(ps -o pid -ax --ppid $PPID) do ....... done

0 votes

Cela ne fonctionne pas pour moi, sauf si je retire le -ax, pour une raison quelconque (Centos5). Sinon, c'est génial !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X