Quelle est la meilleure façon d'envoyer un signal à tous les membres d'un groupe de processus?
422
Je veux tuer tout un arbre de processus. Quelle est la meilleure façon de procéder en utilisant des langages de script courants? Je cherche une solution simple.
Les zombies devraient disparaître lorsque la faucheuse système fonctionne. J'avoue que j'ai vu des systèmes où s'attardent des zombies, mais c'est atypique.
Brian Knoblauch
7
Parfois, ces zombies persistants sont responsables d'une activité effrayante.
@ MichaelLeBarbierGrünewald Pourriez-vous s'il vous plaît lier à ces programmes?
mouche en polystyrène
Réponses:
305
Vous ne dites pas si l'arbre que vous voulez tuer est un seul groupe de processus. (C'est souvent le cas si l'arborescence est le résultat d'un fork d'un démarrage de serveur ou d'une ligne de commande shell.) Vous pouvez découvrir des 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, utilisez simplement la kill(1)commande mais au lieu de lui donner un numéro de processus, donnez-lui la négation du numéro de groupe. Par exemple, pour tuer tous les processus du groupe 5112, utilisez kill -TERM -- -5112.
kill -74313 -bash: kill: 74313: spécification de signal invalide Si j'ajoute le kill -15 -GPID cela a fonctionné parfaitement.
Adam Peck,
51
Comme d'habitude avec presque toutes les commandes, si vous voulez qu'un argument normal qui commence par un - ne soit pas interprété comme un commutateur, faites
ysth
9
pgreppeut offrir un moyen plus simple de trouver l'ID de groupe de processus. Par exemple, pour tuer le groupe de processus de my-script.sh, exécutez kill -TERM -$(pgrep -o my-script.sh).
Josh Kelley
9
Mieux vaut regarder stackoverflow.com/questions/392022/… c'est de loin une solution plus élégante et si vous avez besoin de lister les pids des enfants alors utilisez:ps -o pid --no-headers --ppid $PARENT_PID
Szymon Jeż
4
Et si vous modifiez légèrement le format et triez, vous pouvez voir tous les processus bien regroupés et en commençant par (potentiellement) le parent du groupe dans chaque groupe:ps x -o "%r %p %y %x %c" | sort -nk1,2
haridsv
199
Tuez tous les processus appartenant à la même arborescence de processus à l'aide de l' ID de 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 à PGIDpartir de n'importe quel ID de processus ( PID) de la même arborescence de processus
Un merci spécial à tanager et Speakus pour leurs contributions sur $PIDles espaces restants et la compatibilité OSX.
Explication
kill -9 -"$PGID"=> Envoyer le signal 9 ( KILL) à tous les enfants et petits-enfants ...
PGID=$(ps opgid= "$PID")=> Récupérer l' ID de groupe de processus à partir de n'importe quel ID de processus de l'arborescence, pas seulement l' ID de parent de processus . Une variation de ps opgid= $PIDest ps -o pgid --no-headers $PIDoù pgidpeut être remplacée par pgrp. Mais:
psinserts menant espaces lorsque PIDest inférieur à cinq chiffres et aligné à droite comme remarqué par tanager . Vous pouvez utiliser: PGID=$(ps opgid= "$PID" | 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'ont remarqué davide et Hubert Kario , lorsqu'il killest invoqué par un processus appartenant au même arbre, il killrisque de se tuer avant de mettre fin à la destruction de l'arbre entier.
Par conséquent, assurez-vous d'exécuter la commande à l'aide d'un processus ayant un ID de groupe de processus différent .
Exécutez l'arborescence des processus en arrière-plan à l'aide de '&'
>./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
28348283492834928349 pts/328969Ss330210:00-bash
28349289572895728349 pts/328969 S 330210:00 \_ /bin/sh ./run-many-processes.sh
28957289582895728349 pts/328969 S 330210:00| \_ /bin/sh ./child.sh background
28958289612895728349 pts/328969 S 330210:00|| \_ /bin/sh ./grandchild.sh background
28961289652895728349 pts/328969 S 330210:00||| \_ sleep 999928958289632895728349 pts/328969 S 330210:00|| \_ /bin/sh ./grandchild.sh foreground
28963289672895728349 pts/328969 S 330210:00|| \_ sleep 999928957289592895728349 pts/328969 S 330210:00| \_ /bin/sh ./child.sh foreground
28959289602895728349 pts/328969 S 330210:00| \_ /bin/sh ./grandchild.sh background
28960289642895728349 pts/328969 S 330210:00|| \_ sleep 999928959289622895728349 pts/328969 S 330210:00| \_ /bin/sh ./grandchild.sh foreground
28962289662895728349 pts/328969 S 330210:00| \_ sleep 999928349289692896928349 pts/328969 R+330210:00 \_ ps fj
La commande pkill -P $PIDne tue pas le petit-enfant:
> pkill -P "$PID"./run-many-processes.sh: line 4:28958Terminated./child.sh background
./run-many-processes.sh: line 4:28959Terminated./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
28348283492834928349 pts/328987Ss330210:00-bash
28349289872898728349 pts/328987 R+330210:00 \_ ps fj
1289632895728349 pts/328987 S 330210:00/bin/sh ./grandchild.sh foreground
28963289672895728349 pts/328987 S 330210:00 \_ sleep 99991289622895728349 pts/328987 S 330210:00/bin/sh ./grandchild.sh foreground
28962289662895728349 pts/328987 S 330210:00 \_ sleep 99991289612895728349 pts/328987 S 330210:00/bin/sh ./grandchild.sh background
28961289652895728349 pts/328987 S 330210:00 \_ sleep 99991289602895728349 pts/328987 S 330210:00/bin/sh ./grandchild.sh background
28960289642895728349 pts/328987 S 330210:00 \_ sleep 9999
La commande kill -- -$PGIDtue tous les processus, y compris le petit-enfant.
> 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
28348283492834928349 pts/329039Ss330210:00-bash
28349290392903928349 pts/329039 R+330210:00 \_ ps fj
Conclusion
Je remarque dans cet exemple PIDet suis PGIDégal ( 28957).
C'est pourquoi je pensais à l'origine que kill -- -$PIDc'était suffisant. Mais dans le cas où le processus est fraient dans un Makefilel' ID de processus est différent de l' ID de groupe .
Je pense que kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)c'est la meilleure astuce simple pour tuer un arbre de processus entier lorsqu'il est appelé à partir d'un ID de groupe différent (un autre arbre de processus).
Salut @davide. Bonne question. Je pense que killdevrait toujours envoyer le signal à l'arbre entier avant de recevoir son propre signal. Mais dans certaines circonstances / implémentations spécifiques, il killpeut s’envoyer le signal, être interrompu, puis recevoir son propre signal. Cependant, le risque doit être suffisamment minime et peut être ignoré dans la plupart des cas, car d'autres bogues doivent se produire avant celui-ci. Ce risque peut-il être ignoré dans votre cas? De plus, d'autres réponses ont ce bogue commun (une killpartie de l'arbre de processus est en cours de suppression). J'espère que cette aide .. Cheers;)
olibre
3
Cela ne fonctionne que si les sous-commandes elles-mêmes ne deviennent pas des chefs de groupe. Même des outils aussi simples comme mança. D'un autre côté, si vous voulez tuer le processus gandchild du processus enfant, kill -- -$pidcela ne fonctionnera pas. Ce n'est donc pas une solution générique.
Hubert Kario
1
L'exemple est "l'enfant" essayant de tuer ses enfants (donc les petits-enfants de la commande initiée par l'utilisateur). IOW, essayez de tuer la hiérarchie du processus d'arrière-plan dans child.sh.
Hubert Kario
1
> kill -QUIT - "$ PGID" # même signal que [CRTL + C] du clavier ---- QUIT devrait être remplacé par INT pour être vrai
Maxim Kholyavkin
1
pour l'option OSX - no-headers n'est pas pris en charge, le code doit donc être mis à jour vers: PGID = "$ (ps -o pgid" $ PID "| grep [0-9] | tr -d '')"
Maxim Kholyavkin
166
pkill -TERM -P 27888
Cela tuera tous les processus qui ont l'ID de processus parent 27888.
Dans mon test rapide, pgrep n'a signalé que les enfants immédiats, donc cela peut ne pas tuer toute la hiérarchie.
haridsv
10
Je suis d'accord avec @haridsv: pkill -Penvoie le signal à l'enfant uniquement => le petit-enfant ne reçoit pas le signal => J'ai donc écrit une autre réponse pour l'expliquer.
Santé
1
À partir d'un script bash, pour tuer vos propres enfants, utilisez pkill -TERM -P ${$}.
François Beausoleil
3
@Onlyjob, n'est-il pas dangereux de dormir puis de tuer? Les ID de processus peuvent avoir été réutilisés par le système d'exploitation entre-temps: vous pourriez tuer des processus qui ne sont plus vos enfants. Je soupçonne que l'appel à pkill devrait être refait pour éviter cela.
François Beausoleil
2
@Onlyjob FYI, je suis capable de générer 32768 processus, simple thread, avec un accès E / S léger en moins de 19 secondes sur ma machine:$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
cychoi
102
Pour tuer une arborescence de processus récursivement, 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 killingfor _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 1fi
killtree $@
Les --arguments pour psne pas fonctionner sous OS X. Pour le faire fonctionner, remplacez la pscommande par: ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/goù _regexest défini avant la forboucle:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
artur
5
Les processus arrêtés ne sont pas tués avec SIGTERM. Voir ma réponse
x-yuri
1
-1 utilise #! / Bin / bash au lieu de #! / Usr / bin / env bash (ou mieux encore les constructions POSIX et / bin / sh)
Good Person
Serait-il suffisant d'envoyer un SIGKILL à la place de SIGTERM pour garantir que cela killtree()fonctionne de manière fiable?
davide
3
si psne prend pas en charge --ppid, on peut utiliser à la pgrep -P ${_pid}place
Hubert Kario
16
La commande rkill du package pslist envoie le signal donné (ouSIGTERMpar défaut) au processus spécifié et à tous ses descendants:
Certaines versions de ps émettront un avertissement si vous utilisez "-ax" au lieu de "ax". Ainsi: pour l'enfant dans $ (ps -o pid, ppid ax | \ awk "{if (\ $ 2 == $ pid) {print \ $ 1}}")
Simplifier avec grep, au lieu de sed ...pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
anishsane
2
vous devez ajouter -là pstree, donc les longues lignes ne sont pas tronquées; il peut être rendu plus simple à lire également avec kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`(pas besoin de convertir \nen espace car cela fonctionnera bien), merci!
Aquarius Power
9
Version modifiée de la réponse de zhigang:
#!/usr/bin/env bashset-eu
killtree(){local pid
for pid;do
kill -stop $pid
local cpid
for cpid in $(pgrep -P $pid);do
killtree $cpid
done
kill $pid
kill -cont $pid
wait $pid 2>/dev/null || true
done}
cpids(){local pid=$1 options=${2:-} space=${3:-}local cpid
for cpid in $(pgrep -P $pid);do
echo "$space$cpid"if[["${options/a/}"!="$options"]];then
cpids $cpid "$options""$space "fidone}while true;do sleep 1;done&
cpid=$!for i in $(seq 12);do
cpids $$ a
sleep 1done
killtree $cpid
echo ---
cpids $$ a
vous ne pouvez le faire wait $pidque sur les processus que vous avez démarrés, pas sur tous les processus, donc ce n'est pas une solution générique
Hubert Kario
@Hubert Kario Dans ce cas, l'attente se terminera avec un état différent de zéro et continuera d'exécuter le script. Ai-je tort? Mais waitsupprimera le Terminatedmessage s'il s'agit d'un enfant.
x-yuri
9
Je ne peux pas commenter (pas assez de réputation), donc je suis obligé d'ajouter une nouvelle réponse , même si ce n'est pas vraiment une réponse.
Il y a un léger problème avec la réponse par ailleurs très agréable et approfondie donnée par @olibre le 28 février. La sortie de ps opgid= $PIDcontiendra des espaces de tête pour un PID inférieur à cinq chiffres car psjustifie la colonne (alignez les chiffres). Dans toute la ligne de commande, cela se traduit par un signe négatif, suivi par des espaces, suivi du groupe PID. La solution simple consiste à diriger psvers trpour supprimer les espaces:
La fonction setsid () doit créer une nouvelle session, si le processus appelant n'est pas un leader de groupe de processus. A son retour, le processus appelant sera le chef de session de cette nouvelle session, sera le chef de groupe de processus d'un nouveau groupe de processus et n'aura aucun terminal de contrôle. L'ID de groupe de processus du processus appelant doit être égal à l'ID de processus du processus appelant. Le processus appelant doit être le seul processus du nouveau groupe de processus et le seul processus de la nouvelle session.
Ce que je suppose que vous pouvez créer un groupe à partir du processus de démarrage. Je l'ai utilisé en php afin de pouvoir tuer tout un arbre de processus après l'avoir démarré.
Cela peut être une mauvaise idée. Je serais intéressé par des commentaires.
En fait, c'est une excellente idée et fonctionne très bien. Je l'utilise dans les cas où je peux mettre des processus dans le même groupe de processus (ou ils sont déjà dans le même groupe).
au lieu de lui donner un numéro de processus, donnez-lui la négation du numéro de groupe. Comme d'habitude avec presque toutes les commandes, si vous voulez qu'un argument normal commençant par un -ne soit pas interprété comme un commutateur, faites-le précéder de--
Oups, je viens de réaliser que j'ai donné la même réponse que vous => +1. Mais en plus j'explique comment simplement PGIDpartir PID. Qu'est-ce que tu penses? Vive
olibre
5
La fonction shell suivante est similaire à la plupart des autres réponses, mais elle fonctionne à la fois sur Linux et BSD (OS X, etc.) sans dépendances externes comme pgrep:
killtree(){local parent=$1 child
for child in $(ps -o ppid=-o pid=| awk "\$1==$parent {print \$2}");do
killtree $child
done
kill $parent
}
Je pense que vous avez un mot supplémentaire "enfant" à la fin de la 2ème ligne.
mato
@mato - Ce n'est pas extra. Il limite la portée de $childcette fonction afin de ne pas perturber les autres variables (non locales) du même nom et de garantir que la valeur de la variable locale est nettoyée après la fin de la fonction.
Adam Katz
Oh, je vois maintenant, je pensais que cela faisait partie de la mission. Je suppose que je devrais relire (plus lentement) avant d'écrire un commentaire. :) Merci.
mato
Au fait, ne serait-il pas préférable de créer une liste d'enfants et de commencer à tuer par le haut (parent)? .. Ainsi, nous pourrions éviter une situation où un parent recrée un enfant ou continue d'exécuter le code suivant, ce qui pourrait potentiellement modifier le comportement souhaité.
mato
5
C'est super facile de le faire avec python en utilisant psutil . Installez simplement psutil avec pip et vous disposez d'une suite complète d'outils de manipulation de processus:
def killChildren(pid):
parent = psutil.Process(pid)for child in parent.get_children(True):if child.is_running():
child.terminate()
Il semble bien fonctionner à la fois sur Mac et Linux. Dans les situations où vous ne pouvez pas compter sur la capacité de gérer des groupes de processus - comme lors de l'écriture de scripts pour tester un logiciel qui doit être construit dans plusieurs environnements - cette technique d'arborescence est certainement utile.
Merci pour votre sagesse, les amis. Mon script laissait certains processus enfants à la sortie et la pointe de négation a rendu les choses plus faciles. J'ai écrit cette fonction pour être utilisée dans d'autres scripts si nécessaire:
# kill my group's subprocesses: killGroup# kill also myself: killGroup -x# kill another group's subprocesses: killGroup N # kill that group all: killGroup -x N# N: PID of the main process (= process group ID).function killGroup (){local prid mainpid
case $1 in-x)[-n "$2"]&& kill -9-$2 || kill -9-$$ ;;"") mainpid=$$ ;;*) mainpid=$1 ;;esac
prid=$(ps ax -o pid,pgid | grep $mainpid)
prid=${prid//$mainpid/}
kill -9 $prid 2>/dev/null
return}
Il vaut probablement mieux tuer le parent avant les enfants; sinon, le parent peut probablement engendrer de nouveaux enfants avant qu'il ne soit tué lui-même. Ceux-ci survivront au massacre.
Ma version de ps est différente de celle ci-dessus; peut-être trop vieux, donc l'étrange avidité ...
Utiliser un script shell au lieu d'une fonction shell présente de nombreux avantages ...
Cependant, c'est essentiellement l'idée de zhigangs
notez que le script @zhighang SIGSTOPs le processus parent et fournit le signal au processus arrêté, donc cela ne devrait pas (AFAIK) provoquer une condition de concurrence entre la création de processus enfants et la livraison du signal. Votre version a cependant une course entre l'obtention de la liste des enfants et la transmission du signal aux parents.
Hubert Kario
1
Ce qui suit a été testé sur FreeBSD, Linux et MacOS X et ne dépend que de pgrep et kill (les versions ps -o ne fonctionnent pas sous BSD). Le premier argument est le parent pid dont les enfants doivent être supprimés. le deuxième argument est un booléen pour déterminer si le pid parent doit également être terminé.
Cela enverra SIGTERM à tout processus enfant / petit-enfant dans un script shell et si SIGTERM ne réussit pas, il attendra 10 secondes, puis enverra kill.
Réponse antérieure:
Ce qui suit fonctionne également mais tuera le shell lui-même sur BSD.
KillSubTree(){local parent="${1}"for child in $(ps -o pid=$parent);doif[ $$ -ne $child ];then(kill -s SIGTERM $child ||(sleep 10&& kill -9 $child &))>/dev/null 2>&1;fidone}# Example lanch from within scriptKillSubTree $$ >/dev/null 2>&1
Je développe encore la solution de zhigang, xyuri et solidsneck:
#!/bin/bashif test $# -lt 1 ; then
echo >&2"usage: kiltree pid (sig)"
exit 1;fi;
_pid=$1
_sig=${2:-TERM}# echo >&2 "killtree($_pid) mypid = $$"# ps axwwf | grep -6 "^[ ]*$_pid " >&2 ;function _killtree (){local _children
local _child
local _success
if test $1 -eq $2 ;then# this is killtree - don't commit suicide!
echo >&2"killtree can´t kill it´s own branch - some processes will survive.";return1;fi;# this avoids that children are spawned or disappear.
kill -SIGSTOP $2 ;
_children=$(ps -o pid --no-headers --ppid $2);
_success=0for _child in ${_children};do
_killtree $1 ${_child} $3 ;
_success=$(($_success+$?));done;if test $_success -eq 0;then
kill -$3 $2
fi;# when a stopped process is killed, it will linger in the system until it is continued
kill -SIGCONT $2
test $_success -eq 0;return $?}
_killtree $$ $_pid $_sig
Cette version évitera de tuer son ascendance - ce qui provoque un déluge de processus enfants dans les solutions précédentes.
Les processus sont correctement arrêtés avant que la liste d'enfants ne soit déterminée, afin qu'aucun nouvel enfant ne soit créé ou disparaisse.
Après avoir été tués, les emplois suspendus doivent continuer à disparaître du système.
Vieille question, je sais, mais toutes les réponses semblent continuer d'appeler ps, ce que je n'ai pas aimé.
Cette solution basée sur awk ne nécessite pas de récursivité et n'appelle ps qu'une seule fois.
awk 'BEGIN {
p=1390
while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2
o=1
while (o==1) {
o=0
split(p, q, " ")
for (i in q) if (a[q[i]]!="") {
p=p""a[q[i]]
o=1
a[q[i]]=""
}
}
system("kill -TERM "p)
}'
Ou sur une seule ligne:
awk 'BEGIN {p=1390;while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2;o=1;while (o==1) {o=0;split(p, q, " ");for (i in q) {if (a[q[i]]!="") {p=p""a[q[i]];o=1;a[q[i]]=""}}}system("kill -TERM "p)}'
Fondamentalement, l'idée est de créer un tableau (a) d'entrées parent: enfant, puis de parcourir le tableau pour trouver des enfants pour nos parents correspondants, en les ajoutant à notre liste de parents (p) au fur et à mesure.
Si vous ne voulez pas tuer le processus de niveau supérieur, alors faites
sub(/[0-9]*/,"", p)
juste avant que la ligne system () ne la supprime du kill set.
Gardez à l'esprit qu'il y a une condition de concurrence ici, mais c'est vrai (pour autant que je puisse voir) de toutes les solutions. Il fait ce dont j'avais besoin parce que le script dont j'avais besoin ne crée pas beaucoup d'enfants de courte durée.
Un exercice pour le lecteur serait d'en faire une boucle en 2 passes: après la première passe, envoyez SIGSTOP à tous les processus de la liste p, puis bouclez pour relancer ps et après la deuxième passe envoyez SIGTERM, puis SIGCONT. Si vous ne vous souciez pas des fins agréables, le second passage pourrait simplement être SIGKILL, je suppose.
Si vous connaissez le pid de la chose que vous voulez tuer, vous pouvez généralement aller à partir de l'ID de session et de tout dans la même session. Je revérifierais, mais je l'ai utilisé pour les scripts démarrant rsyncs dans des boucles que je veux mourir, et pas pour en démarrer une autre (à cause de la boucle) comme si je venais de tuer kills rsync.
Dans sh, la commande jobs listera les processus d'arrière-plan. Dans certains cas, il peut être préférable de tuer le processus le plus récent en premier, par exemple le plus ancien a créé une socket partagée. Dans ces cas, triez les PID dans l'ordre inverse. Parfois, vous voulez attendre un moment pour que les travaux écrivent quelque chose sur le disque ou des trucs comme ça avant de s'arrêter.
Et ne tuez pas si vous n'êtes pas obligé!
for SIGNAL in TERM KILL;dofor CHILD in $(jobs -s|sort -r);do
kill -s $SIGNAL $CHILD
sleep $MOMENT
donedone
Plusieurs fois, nous devons tuer les processus enfants qui sont suspendus ou bloqués pour une raison quelconque. par exemple. Problème de connexion FTP.
Il existe deux approches,
1) Pour créer un nouveau parent distinct pour chaque enfant qui surveillera et tuera le processus enfant une fois le délai écoulé.
et regarder les processus dont le nom est «test» dans un autre terminal en utilisant la commande suivante.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Le script ci-dessus créera 4 nouveaux processus enfants et leurs parents. Chaque processus enfant s'exécutera pendant 10 secondes. Mais une fois le délai de 5 secondes atteint, leurs processus parent respectifs tueront ces enfants. Ainsi, l'enfant ne pourra pas terminer l'exécution (10sec). Jouez autour de ces horaires (commutateurs 10 et 5) pour voir un autre comportement. Dans ce cas, l'enfant terminera l'exécution en 5 secondes avant d'atteindre le délai d'attente de 10 secondes.
2) Laissez le parent actuel surveiller et tuer le processus enfant une fois le délai écoulé. Cela ne créera pas de parent séparé pour surveiller chaque enfant. Vous pouvez également gérer correctement tous les processus enfants au sein du même parent.
Créez test.sh comme suit,
#!/bin/bash
declare -A CPIDs;
declare -a CMDs=("AAA""BBB""CCC""DDD")
CMD_TIME=15;for CMD in ${CMDs[*]};do(echo "Started..$CMD"; sleep $CMD_TIME; echo "$CMD Done";)&CPIDs[$!]="$RN";
sleep 1;done
GPID=$(ps -o pgid= $$);
CNT_TIME_OUT=10;
CNT=0;while(true);do
declare -A TMP_CPIDs;for PID in"${!CPIDs[@]}";do
echo "Checking "${CPIDs[$PID]}"=>"$PID;if ps -p $PID >/dev/null ;then
echo "-->"${CPIDs[$PID]}"=>"$PID" is running..";
TMP_CPIDs[$PID]=${CPIDs[$PID]};else
echo "-->"${CPIDs[$PID]}"=>"$PID" is completed.";fidoneif[ ${#TMP_CPIDs[@]}==0];then
echo "All commands completed.";break;else
unset CPIDs;
declare -A CPIDs;for PID in"${!TMP_CPIDs[@]}";doCPIDs[$PID]=${TMP_CPIDs[$PID]};done
unset TMP_CPIDs;if[ $CNT -gt $CNT_TIME_OUT ];then
echo ${CPIDs[@]}"PIDs not reponding. Timeout reached $CNT sec. killing all childern with GPID $GPID..";
kill ---$GPID;fifi
CNT=$((CNT+1));
echo "waiting since $b secs..";
sleep 1;done
exit;
et regarder les processus dont le nom est «test» dans un autre terminal en utilisant la commande suivante.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Le script ci-dessus créera 4 nouveaux processus enfants. Nous stockons les pids de tous les processus enfants et les bouclons pour vérifier s'ils ont terminé leur exécution ou sont toujours en cours d'exécution. Le processus enfant sera exécuté jusqu'à l'heure CMD_TIME. Mais si le délai d'expiration de CNT_TIME_OUT est atteint, tous les enfants seront tués par le processus parent. Vous pouvez changer de timing et jouer avec le script pour voir le comportement. Un inconvénient de cette approche est qu'elle utilise l'ID de groupe pour tuer tous les arbres enfants. Mais le processus parent lui-même appartient au même groupe, il sera donc également tué.
Vous devrez peut-être attribuer un autre identifiant de groupe au processus parent si vous ne voulez pas que le parent soit tué.
#/bin/sh
while true
do
echo "Enter parent process id [type quit for exit]"
read ppid
if [ $ppid -eq "quit" -o $ppid -eq "QUIT" ];then
exit 0
fi
for i in `ps -ef| awk '$3 == '$ppid' { print $2 }'`
do
echo killing $i
kill $i
done
done
chronos
ouherodes
.Réponses:
Vous ne dites pas si l'arbre que vous voulez tuer est un seul groupe de processus. (C'est souvent le cas si l'arborescence est le résultat d'un fork d'un démarrage de serveur ou d'une ligne de commande shell.) Vous pouvez découvrir des groupes de processus en utilisant GNU ps comme suit:
Si c'est un groupe de processus que vous voulez tuer, utilisez simplement la
kill(1)
commande mais au lieu de lui donner un numéro de processus, donnez-lui la négation du numéro de groupe. Par exemple, pour tuer tous les processus du groupe 5112, utilisezkill -TERM -- -5112
.la source
pgrep
peut offrir un moyen plus simple de trouver l'ID de groupe de processus. Par exemple, pour tuer le groupe de processus de my-script.sh, exécutezkill -TERM -$(pgrep -o my-script.sh)
.ps -o pid --no-headers --ppid $PARENT_PID
ps x -o "%r %p %y %x %c" | sort -nk1,2
Tuez tous les processus appartenant à la même arborescence de processus à l'aide de l' ID de groupe de processus (
PGID
)kill -- -$PGID
Utiliser le signal par défaut (TERM
= 15)kill -9 -$PGID
Utilisez le signalKILL
(9)Vous pouvez récupérer le à
PGID
partir de n'importe quel ID de processus (PID
) de la même arborescence de processuskill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')
(signalTERM
)kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')
(signalKILL
)Un merci spécial à tanager et Speakus pour leurs contributions sur
$PID
les espaces restants et la compatibilité OSX.Explication
kill -9 -"$PGID"
=> Envoyer le signal 9 (KILL
) à tous les enfants et petits-enfants ...PGID=$(ps opgid= "$PID")
=> Récupérer l' ID de groupe de processus à partir de n'importe quel ID de processus de l'arborescence, pas seulement l' ID de parent de processus . Une variation deps opgid= $PID
estps -o pgid --no-headers $PID
oùpgid
peut être remplacée parpgrp
.Mais:
ps
inserts menant espaces lorsquePID
est inférieur à cinq chiffres et aligné à droite comme remarqué par tanager . Vous pouvez utiliser:PGID=$(ps opgid= "$PID" | tr -d ' ')
ps
depuis OSX imprime toujours l'en-tête, donc 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
Limitation
kill
est invoqué par un processus appartenant au même arbre, ilkill
risque de se tuer avant de mettre fin à la destruction de l'arbre entier.Longue histoire
Exécutez l'arborescence des processus en arrière-plan à l'aide de '&'
La commande
pkill -P $PID
ne tue pas le petit-enfant:La commande
kill -- -$PGID
tue tous les processus, y compris le petit-enfant.Conclusion
Je remarque dans cet exemple
PID
et suisPGID
égal (28957
).C'est pourquoi je pensais à l'origine que
kill -- -$PID
c'était suffisant. Mais dans le cas où le processus est fraient dans unMakefile
l' ID de processus est différent de l' ID de groupe .Je pense que
kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)
c'est la meilleure astuce simple pour tuer un arbre de processus entier lorsqu'il est appelé à partir d'un ID de groupe différent (un autre arbre de processus).la source
kill
devrait toujours envoyer le signal à l'arbre entier avant de recevoir son propre signal. Mais dans certaines circonstances / implémentations spécifiques, ilkill
peut s’envoyer le signal, être interrompu, puis recevoir son propre signal. Cependant, le risque doit être suffisamment minime et peut être ignoré dans la plupart des cas, car d'autres bogues doivent se produire avant celui-ci. Ce risque peut-il être ignoré dans votre cas? De plus, d'autres réponses ont ce bogue commun (unekill
partie de l'arbre de processus est en cours de suppression). J'espère que cette aide .. Cheers;)man
ça. D'un autre côté, si vous voulez tuer le processus gandchild du processus enfant,kill -- -$pid
cela ne fonctionnera pas. Ce n'est donc pas une solution générique.child.sh
.Cela tuera tous les processus qui ont l'ID de processus parent 27888.
Ou plus robuste:
qui doit tuer 33 secondes plus tard et demander poliment aux processus de se terminer.
Voir cette réponse pour mettre fin à tous les descendants.
la source
pkill -P
envoie le signal à l'enfant uniquement => le petit-enfant ne reçoit pas le signal => J'ai donc écrit une autre réponse pour l'expliquer.pkill -TERM -P ${$}
.$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
Pour tuer une arborescence de processus récursivement, utilisez killtree ():
la source
--
arguments pourps
ne pas fonctionner sous OS X. Pour le faire fonctionner, remplacez laps
commande par:ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g
où_regex
est défini avant lafor
boucle:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
killtree()
fonctionne de manière fiable?ps
ne prend pas en charge--ppid
, on peut utiliser à lapgrep -P ${_pid}
placeLa commande rkill du package pslist envoie le signal donné (ou
SIGTERM
par défaut) au processus spécifié et à tous ses descendants:la source
La réponse de Brad est ce que je recommanderais aussi, sauf que vous pouvez supprimer
awk
complètement si vous utilisez l'--ppid
option pourps
.la source
si vous savez passer le pid du processus parent, voici un script shell qui devrait fonctionner:
la source
J'utilise une version légèrement modifiée d'une méthode décrite ici: https://stackoverflow.com/a/5311362/563175
Donc ça ressemble à ça:
où 24901 est le PID du parent.
Il a l'air assez moche mais fait son travail parfaitement.
la source
pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
-l
àpstree
, donc les longues lignes ne sont pas tronquées; il peut être rendu plus simple à lire également aveckill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`
(pas besoin de convertir\n
en espace car cela fonctionnera bien), merci!Version modifiée de la réponse de zhigang:
la source
wait $pid
que sur les processus que vous avez démarrés, pas sur tous les processus, donc ce n'est pas une solution génériquewait
supprimera leTerminated
message s'il s'agit d'un enfant.Je ne peux pas commenter (pas assez de réputation), donc je suis obligé d'ajouter une nouvelle réponse , même si ce n'est pas vraiment une réponse.
Il y a un léger problème avec la réponse par ailleurs très agréable et approfondie donnée par @olibre le 28 février. La sortie de
ps opgid= $PID
contiendra des espaces de tête pour un PID inférieur à cinq chiffres carps
justifie la colonne (alignez les chiffres). Dans toute la ligne de commande, cela se traduit par un signe négatif, suivi par des espaces, suivi du groupe PID. La solution simple consiste à dirigerps
verstr
pour supprimer les espaces:la source
Pour compléter la réponse de Norman Ramsey, il peut être utile de regarder setsid si vous souhaitez créer un groupe de processus.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html
Ce que je suppose que vous pouvez créer un groupe à partir du processus de démarrage. Je l'ai utilisé en php afin de pouvoir tuer tout un arbre de processus après l'avoir démarré.
Cela peut être une mauvaise idée. Je serais intéressé par des commentaires.
la source
Inspiré par le commentaire d' Ysth
la source
PGID
partirPID
. Qu'est-ce que tu penses? ViveLa fonction shell suivante est similaire à la plupart des autres réponses, mais elle fonctionne à la fois sur Linux et BSD (OS X, etc.) sans dépendances externes comme
pgrep
:la source
$child
cette fonction afin de ne pas perturber les autres variables (non locales) du même nom et de garantir que la valeur de la variable locale est nettoyée après la fin de la fonction.C'est super facile de le faire avec python en utilisant psutil . Installez simplement psutil avec pip et vous disposez d'une suite complète d'outils de manipulation de processus:
la source
Sur la base de la réponse de zhigang, cela évite l'auto-destruction:
la source
Si vous voulez tuer un processus par son nom:
ou
la source
Ceci est ma version de tuer tous les processus enfants en utilisant le script bash. Il n'utilise pas de récursivité et dépend de la commande pgrep.
Utilisation
Contenu de killtrees.sh
la source
Voici une variante de la réponse de @ zhigang qui se passe d'AWK, en se basant uniquement sur les possibilités d'analyse native de Bash:
Il semble bien fonctionner à la fois sur Mac et Linux. Dans les situations où vous ne pouvez pas compter sur la capacité de gérer des groupes de processus - comme lors de l'écriture de scripts pour tester un logiciel qui doit être construit dans plusieurs environnements - cette technique d'arborescence est certainement utile.
la source
Merci pour votre sagesse, les amis. Mon script laissait certains processus enfants à la sortie et la pointe de négation a rendu les choses plus faciles. J'ai écrit cette fonction pour être utilisée dans d'autres scripts si nécessaire:
À votre santé.
la source
si vous avez pstree et perl sur votre système, vous pouvez essayer ceci:
la source
Il vaut probablement mieux tuer le parent avant les enfants; sinon, le parent peut probablement engendrer de nouveaux enfants avant qu'il ne soit tué lui-même. Ceux-ci survivront au massacre.
Ma version de ps est différente de celle ci-dessus; peut-être trop vieux, donc l'étrange avidité ...
Utiliser un script shell au lieu d'une fonction shell présente de nombreux avantages ...
Cependant, c'est essentiellement l'idée de zhigangs
la source
Ce qui suit a été testé sur FreeBSD, Linux et MacOS X et ne dépend que de pgrep et kill (les versions ps -o ne fonctionnent pas sous BSD). Le premier argument est le parent pid dont les enfants doivent être supprimés. le deuxième argument est un booléen pour déterminer si le pid parent doit également être terminé.
Cela enverra SIGTERM à tout processus enfant / petit-enfant dans un script shell et si SIGTERM ne réussit pas, il attendra 10 secondes, puis enverra kill.
Réponse antérieure:
Ce qui suit fonctionne également mais tuera le shell lui-même sur BSD.
la source
Je développe encore la solution de zhigang, xyuri et solidsneck:
Cette version évitera de tuer son ascendance - ce qui provoque un déluge de processus enfants dans les solutions précédentes.
Les processus sont correctement arrêtés avant que la liste d'enfants ne soit déterminée, afin qu'aucun nouvel enfant ne soit créé ou disparaisse.
Après avoir été tués, les emplois suspendus doivent continuer à disparaître du système.
la source
Vieille question, je sais, mais toutes les réponses semblent continuer d'appeler ps, ce que je n'ai pas aimé.
Cette solution basée sur awk ne nécessite pas de récursivité et n'appelle ps qu'une seule fois.
Ou sur une seule ligne:
Fondamentalement, l'idée est de créer un tableau (a) d'entrées parent: enfant, puis de parcourir le tableau pour trouver des enfants pour nos parents correspondants, en les ajoutant à notre liste de parents (p) au fur et à mesure.
Si vous ne voulez pas tuer le processus de niveau supérieur, alors faites
juste avant que la ligne system () ne la supprime du kill set.
Gardez à l'esprit qu'il y a une condition de concurrence ici, mais c'est vrai (pour autant que je puisse voir) de toutes les solutions. Il fait ce dont j'avais besoin parce que le script dont j'avais besoin ne crée pas beaucoup d'enfants de courte durée.
Un exercice pour le lecteur serait d'en faire une boucle en 2 passes: après la première passe, envoyez SIGSTOP à tous les processus de la liste p, puis bouclez pour relancer ps et après la deuxième passe envoyez SIGTERM, puis SIGCONT. Si vous ne vous souciez pas des fins agréables, le second passage pourrait simplement être SIGKILL, je suppose.
la source
Si vous connaissez le pid de la chose que vous voulez tuer, vous pouvez généralement aller à partir de l'ID de session et de tout dans la même session. Je revérifierais, mais je l'ai utilisé pour les scripts démarrant rsyncs dans des boucles que je veux mourir, et pas pour en démarrer une autre (à cause de la boucle) comme si je venais de tuer kills rsync.
Si vous ne connaissez pas le pid, vous pouvez toujours imbriquer plus
la source
la source
kill -9
vraiment.kill -15
, ça n'aide pas.Dans sh, la commande jobs listera les processus d'arrière-plan. Dans certains cas, il peut être préférable de tuer le processus le plus récent en premier, par exemple le plus ancien a créé une socket partagée. Dans ces cas, triez les PID dans l'ordre inverse. Parfois, vous voulez attendre un moment pour que les travaux écrivent quelque chose sur le disque ou des trucs comme ça avant de s'arrêter.
Et ne tuez pas si vous n'êtes pas obligé!
la source
Tuer le processus enfant dans le script shell:
Plusieurs fois, nous devons tuer les processus enfants qui sont suspendus ou bloqués pour une raison quelconque. par exemple. Problème de connexion FTP.
Il existe deux approches,
1) Pour créer un nouveau parent distinct pour chaque enfant qui surveillera et tuera le processus enfant une fois le délai écoulé.
Créez test.sh comme suit,
et regarder les processus dont le nom est «test» dans un autre terminal en utilisant la commande suivante.
Le script ci-dessus créera 4 nouveaux processus enfants et leurs parents. Chaque processus enfant s'exécutera pendant 10 secondes. Mais une fois le délai de 5 secondes atteint, leurs processus parent respectifs tueront ces enfants. Ainsi, l'enfant ne pourra pas terminer l'exécution (10sec). Jouez autour de ces horaires (commutateurs 10 et 5) pour voir un autre comportement. Dans ce cas, l'enfant terminera l'exécution en 5 secondes avant d'atteindre le délai d'attente de 10 secondes.
2) Laissez le parent actuel surveiller et tuer le processus enfant une fois le délai écoulé. Cela ne créera pas de parent séparé pour surveiller chaque enfant. Vous pouvez également gérer correctement tous les processus enfants au sein du même parent.
Créez test.sh comme suit,
et regarder les processus dont le nom est «test» dans un autre terminal en utilisant la commande suivante.
Le script ci-dessus créera 4 nouveaux processus enfants. Nous stockons les pids de tous les processus enfants et les bouclons pour vérifier s'ils ont terminé leur exécution ou sont toujours en cours d'exécution. Le processus enfant sera exécuté jusqu'à l'heure CMD_TIME. Mais si le délai d'expiration de CNT_TIME_OUT est atteint, tous les enfants seront tués par le processus parent. Vous pouvez changer de timing et jouer avec le script pour voir le comportement. Un inconvénient de cette approche est qu'elle utilise l'ID de groupe pour tuer tous les arbres enfants. Mais le processus parent lui-même appartient au même groupe, il sera donc également tué.
Vous devrez peut-être attribuer un autre identifiant de groupe au processus parent si vous ne voulez pas que le parent soit tué.
Plus de détails ici,
Tuer un processus enfant dans un script shell
la source
Ce script fonctionne également:
#/bin/sh while true do echo "Enter parent process id [type quit for exit]" read ppid if [ $ppid -eq "quit" -o $ppid -eq "QUIT" ];then exit 0 fi for i in `ps -ef| awk '$3 == '$ppid' { print $2 }'` do echo killing $i kill $i done done
la source
Je sais que c'est vieux, mais c'est la meilleure solution que j'ai trouvée:
la source