¿Cuál es la mejor manera de enviar una señal a todos los miembros de un grupo de procesos?
422
Quiero matar todo un árbol de procesos. ¿Cuál es la mejor manera de hacer esto usando cualquier lenguaje de script común? Estoy buscando una solución simple.
Sin embargo, los zombis deberían desaparecer cuando se ejecuta el segador del sistema. Admito que he visto sistemas donde los zombis permanecen, pero eso es atípico.
Brian Knoblauch
77
A veces, esos zombis persistentes son responsables de alguna actividad aterradora.
@ MichaelLeBarbierGrünewald ¿Podría vincular a esos programas?
espuma de poliestireno volar
Respuestas:
305
No dice si el árbol que desea matar es un solo grupo de procesos. (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 utilizando GNU ps de la siguiente manera:
ps x -o "%p %r %y %x %c "
Si es un grupo de proceso que desea eliminar, simplemente use el kill(1)comando pero en lugar de darle un número de proceso, dele la negación del número de grupo. Por ejemplo, para eliminar todos los procesos del grupo 5112, use kill -TERM -- -5112.
kill -74313 -bash: kill: 74313: especificación de señal no válida Si agrego kill -15 -GPID funcionó perfectamente.
Adam Peck
51
Como es habitual con casi cualquier comando, si desea que un argumento normal que comience con un - no se interprete como un interruptor, preceda con -: kill - -GPID
ysth
99
pgreppuede ofrecer una manera más fácil de encontrar la ID del grupo de procesos. Por ejemplo, para matar el grupo de procesos my-script.sh, ejecute kill -TERM -$(pgrep -o my-script.sh).
Josh Kelley el
99
Mire mejor stackoverflow.com/questions/392022/… es, con mucho, una solución más elegante y si necesita enumerar las imágenes de los niños, use:ps -o pid --no-headers --ppid $PARENT_PID
Szymon Jeż
44
Y si modifica el formato un poco y ordena, verá todos los procesos bien agrupados y comenzando con (potencialmente) el grupo principal en cada grupo:ps x -o "%r %p %y %x %c" | sort -nk1,2
haridsv
199
Elimine todos los procesos que pertenecen al mismo árbol de procesos utilizando el ID del grupo de procesos ( PGID)
Un agradecimiento especial a tanager y Speakus por sus contribuciones en $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 la ID de grupo de proceso de cualquier ID de proceso del árbol, no solo la 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 se alinea a la derecha como lo notó la tangara . Puedes usar: PGID=$(ps opgid= "$PID" | tr -d ' ')
psdesde OSX siempre imprime el encabezado, por lo tanto, 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).
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 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 completo.
Por lo tanto, asegúrese de ejecutar el comando utilizando un proceso que tenga una ID de grupo de proceso diferente .
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
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
El comando pkill -P $PIDno mata al nieto:
> 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
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
28348283492834928349 pts/329039Ss330210:00-bash
28349290392903928349 pts/329039 R+330210:00 \_ ps fj
Conclusión
Noto en este ejemplo PIDy PGIDson iguales ( 28957).
Es por eso que originalmente pensé que kill -- -$PIDera suficiente. Pero en el caso de que el proceso se desovar dentro de un Makefileel ID de proceso es diferente del ID de grupo .
Creo que kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)es el mejor truco simple para matar un árbol de proceso completo cuando se llama desde una ID de grupo diferente (otro árbol de proceso).
Hola @davide Buena pregunta. Creo killque siempre debería enviar la señal a todo el árbol antes de recibir su propia señal. Pero en algunas circunstancias / implementaciones específicas, killpuede enviarse la señal a sí mismo, ser interrumpido y luego recibir su propia señal. Sin embargo, el riesgo debe ser lo suficientemente mínimo, y puede ignorarse en la mayoría de los casos porque deberían ocurrir otros errores antes de este. ¿Se puede ignorar este riesgo en su caso? Además, otras respuestas tienen este error común ( killparte del árbol de proceso que se está eliminando). Espero que esta ayuda .. Saludos;)
olibre
3
Esto solo funciona si los subcomandos no se convierten en líderes de grupo. Incluso herramientas tan simples como manhacer eso. Por otro lado, si desea eliminar el proceso gandchild del proceso hijo, kill -- -$pidno funcionará. Por lo tanto, no es una solución genérica.
Hubert Kario el
1
El ejemplo es el "niño" que intenta matar a sus hijos (por lo que los nietos del comando iniciado por el usuario). IOW, intenta eliminar la jerarquía del proceso en segundo plano child.sh.
Hubert Kario el
1
> kill -QUIT - "$ PGID" # misma señal que [CRTL + C] desde el teclado ---- QUIT debe ser reemplazado por INT para ser verdad
Maxim Kholyavkin
1
para la opción OSX --no-headers no es compatible, por lo que el código debe actualizarse a: PGID = "$ (ps -o pgid" $ PID "| grep [0-9] | tr -d '')"
Maxim Kholyavkin
166
pkill -TERM -P 27888
Esto eliminará todos los procesos que tengan el ID de proceso padre 27888.
En mi prueba rápida, pgrep solo informó a los hijos inmediatos, por lo que esto puede no matar a toda la jerarquía.
haridsv
10
Estoy de acuerdo con @haridsv: pkill -Penvía la señal solo al niño => el nieto no recibe la señal => Por lo tanto, he escrito otra respuesta para explicar eso. Saludos ;-)
olibre
1
Desde un script bash, para matar a tus propios hijos, usa pkill -TERM -P ${$}.
François Beausoleil
3
@Onlyjob, ¿no es peligroso dormir y luego matar? Mientras tanto, el sistema operativo puede haber reutilizado los ID de proceso: es posible que esté eliminando procesos que ya no son sus hijos. Sospecho que la llamada a pkill tendría que hacerse de nuevo para asegurarse de esto.
François Beausoleil
2
@Onlyjob FYI, puedo generar 32768 procesos, de un solo subproceso, con acceso de E / S ligero en menos de 19 segundos en mi máquina:$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
cychoi
102
Para matar un árbol de proceso de forma recursiva, use 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 $@
Los --argumentos para psno funcionar en OS X. Para hacerlo funcionar, reemplace el pscomando por: ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/gdonde _regexse define antes del forbucle:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
artur
55
Los procesos detenidos no se matan con SIGTERM. Ver mi respuesta
x-yuri
1
-1 usa #! / Bin / bash en lugar de #! / Usr / bin / env bash (o mejor aún, POSIX solo construye y / bin / sh)
Buena persona
¿Sería suficiente enviar un SIGKILL en lugar de SIGTERM para garantizar que killtree()funcione de manera confiable?
davide
3
si psno es compatible --ppid, se puede usar pgrep -P ${_pid}en su lugar
Hubert Kario
16
El comando rkill del paquete pslist envía la señal dada (oSIGTERMpor defecto) al proceso especificado y a todos sus descendientes:
Algunas versiones de ps arrojarán una advertencia si usa "-ax" en lugar de "ax". Por lo tanto: para el niño en $ (ps -o pid, ppid ax | \ awk "{if (\ $ 2 == $ pid) {print \ $ 1}}")
Simplificando con grep, en lugar de sed ...pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
anishsane
2
debe agregar -la pstree, para que las líneas largas no se trunqueen se puede hacer más fácil de leer también con kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`(no es necesario convertir \nal espacio ya que funcionará bien), gracias!
Acuario Power
9
Versión modificada de la respuesta 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
wait $pidsolo puede en procesos que ha comenzado, no en todos los precesos, por lo que esta no es una solución genérica
Hubert Kario
@Hubert Kario En ese caso, la espera simplemente saldrá con un estado distinto de cero y continuará ejecutando el script. ¿Me equivoco? Pero waitsuprimirá el Terminatedmensaje si es un niño.
x-yuri
9
No puedo comentar (no hay suficiente reputación), por lo que me veo obligado a agregar una nueva respuesta , aunque esta no sea realmente una respuesta.
Hay un pequeño problema con la respuesta por lo demás muy agradable y exhaustiva dada por @olibre el 28 de febrero. La salida de ps opgid= $PIDcontendrá espacios iniciales para un PID de menos de cinco dígitos porque psestá justificando la columna (alinee los números). Dentro de la línea de comando completa, esto da como resultado un signo negativo, seguido de espacio (s), seguido del PID del grupo. Una solución simple es a la tubería psa trlos espacios eliminar los siguientes:
La función setsid () creará una nueva sesión, si el proceso de llamada no es un líder de grupo de proceso. Al regresar, el proceso de llamada será el líder de la sesión de esta nueva sesión, será el líder del grupo de proceso de un nuevo grupo de proceso y no tendrá un terminal de control. La ID del grupo de proceso del proceso de llamada se establecerá igual a la ID de proceso del proceso de llamada. El proceso de llamada será el único proceso en el nuevo grupo de procesos y el único proceso en la nueva sesión.
Lo que quiero decir es que puedes crear un grupo desde el proceso de inicio. Usé esto en php para poder matar un árbol de proceso completo después de iniciarlo.
Esta puede ser una mala idea. Me interesarían los comentarios.
En realidad, esta es una gran idea y funciona muy bien. Lo estoy usando en casos en los que puedo poner procesos en el mismo grupo de procesos (o ya están en el mismo grupo).
en lugar de darle un número de proceso, dele la negación del número de grupo. Como es habitual con casi cualquier comando, si desea que un argumento normal que comienza con -a no se interprete como un interruptor, preceda con--
Vaya, me acabo de dar cuenta de que he dado la misma respuesta que tú => +1. Pero además de explicar cómo conseguir simplemente PGIDa partir PID. ¿Qué piensas? Saludos
olibre 01 de
5
La siguiente función de shell es similar a muchas de las otras respuestas, pero funciona tanto en Linux como en BSD (OS X, etc.) sin dependencias externas como 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
}
Creo que tiene la palabra adicional "niño" al final de la segunda línea.
mato
@mato - Eso no es extra. Limita el alcance de $childesa función para no perturbar otras variables (no locales) con el mismo nombre y garantizar que el valor de la variable local se limpie después de que finalice la función.
Adam Katz
Oh, ya veo, pensé que era parte de la tarea. Supongo que debería volver a leer (más lento) antes de escribir un comentario. :) Gracias.
mato
Por cierto, ¿no sería mejor crear una lista de niños y luego comenzar a matar desde arriba (padre)? .. Por lo tanto, podríamos evitar una situación en la que un padre recrea a un niño o continúa ejecutando el siguiente código que podría alterar el comportamiento deseado.
mato
5
Es muy fácil hacer esto con python usando psutil . Simplemente instale psutil con pip y luego tendrá un conjunto completo de herramientas de manipulación de procesos:
def killChildren(pid):
parent = psutil.Process(pid)for child in parent.get_children(True):if child.is_running():
child.terminate()
Parece funcionar bien tanto en Mac como en Linux. En situaciones en las que no puede confiar en la capacidad de administrar grupos de procesos, como cuando escribe scripts para probar un software que debe construirse en múltiples entornos, esta técnica de caminar por los árboles es definitivamente útil.
Gracias por su sabiduría, amigos. Mi script dejaba algunos procesos secundarios al salir y el consejo de negación facilitó las cosas. Escribí esta función para usarla en otros scripts si es necesario:
# 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}
Probablemente sea mejor matar al padre antes que a los hijos; de lo contrario, es probable que el padre genere nuevos hijos antes de que él mismo se mate. Estos sobrevivirán a la matanza.
Mi versión de ps es diferente de la anterior; tal vez demasiado viejo, por lo tanto, el extraño grepping ...
Usar un script de shell en lugar de una función de shell tiene muchas ventajas ...
tenga en cuenta que el script @zhighang SIGSTOPs el proceso padre y entrega la señal al proceso detenido, por lo que esto no debería (AFAIK) causar una condición de carrera entre el proceso de creación de niños y la entrega de la señal. Sin embargo, su versión tiene competencia entre obtener la lista de niños y enviar la señal a los padres.
Hubert Kario el
1
Lo siguiente ha sido probado en FreeBSD, Linux y MacOS X y solo depende de pgrep y kill (las versiones ps -o no funcionan bajo BSD). El primer argumento es el padre pid de que los hijos tienen que ser terminados. El segundo argumento es un valor booleano para determinar si el pid padre también debe terminarse.
Esto enviará SIGTERM a cualquier proceso hijo / nieto dentro de un script de shell y si SIGTERM no tiene éxito, esperará 10 segundos y luego enviará kill.
Respuesta anterior:
Lo siguiente también funciona, pero matará el propio shell en 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
Desarrollo aún más la solución de zhigang, xyuri y 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
Esta versión evitará matar a sus ancestros, lo que provoca una avalancha de procesos secundarios en las soluciones anteriores.
Los procesos se detienen correctamente antes de determinar la lista secundaria, para que no se creen o desaparezcan nuevos elementos secundarios.
Después de ser asesinados, los trabajos detenidos deben continuar para desaparecer del sistema.
Antigua pregunta, lo sé, pero todas las respuestas parecen seguir llamando ps, lo que no me gustó.
Esta solución basada en awk no requiere recursividad y solo llama ps una vez.
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)
}'
O en una sola línea:
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)}'
Básicamente, la idea es que construyamos una matriz (a) de entradas padre: hijo, luego recorramos la matriz buscando hijos para nuestros padres coincidentes, agregándolos a nuestra lista de padres (p) a medida que avanzamos.
Si no quieres matar el proceso de nivel superior, entonces haz
sub(/[0-9]*/,"", p)
justo antes de que la línea system () lo elimine del conjunto de eliminación.
Tenga en cuenta que hay una condición de carrera aquí, pero eso es cierto (hasta donde puedo ver) de todas las soluciones. Hace lo que necesitaba porque el script para el que lo necesitaba no crea muchos niños de corta duración.
Un ejercicio para el lector sería convertirlo en un ciclo de 2 pasadas: después de la primera pasada, envíe SIGSTOP a todos los procesos en la lista p, luego realice un ciclo para ejecutar ps nuevamente y después de la segunda pasada envíe SIGTERM, luego SIGCONT. Si no te interesan los finales agradables, supongo que el segundo pase podría ser SIGKILL.
Si conoce el pid de lo que desea matar, generalmente puede ir desde la identificación de la sesión y todo en la misma sesión. Verificaría dos veces, pero usé esto para scripts que inician rsyncs en bucles que quiero morir, y no iniciar otro (debido al bucle) como lo haría si acabara de matar a rsync.
En sh, el comando jobs enumerará los procesos en segundo plano. En algunos casos, podría ser mejor eliminar primero el proceso más nuevo, por ejemplo, el anterior creó un socket compartido. En esos casos, clasifique los PID en orden inverso. A veces quieres esperar un momento para que los trabajos escriban algo en el disco o cosas así antes de que se detengan.
¡Y no mates si no tienes que hacerlo!
for SIGNAL in TERM KILL;dofor CHILD in $(jobs -s|sort -r);do
kill -s $SIGNAL $CHILD
sleep $MOMENT
donedone
y observe los procesos que tienen nombre como 'prueba' en otro terminal usando el siguiente comando.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
El script anterior creará 4 nuevos procesos secundarios y sus padres. Cada proceso secundario se ejecutará durante 10 segundos. Pero una vez que se agota el tiempo de espera de 5 segundos, sus respectivos procesos principales matarán a esos niños. Por lo tanto, el niño no podrá completar la ejecución (10 segundos). Juega alrededor de esos tiempos (interruptor 10 y 5) para ver otro comportamiento. En ese caso, el hijo finalizará la ejecución en 5 segundos antes de que alcance el tiempo de espera de 10 segundos.
2) Deje que el padre actual supervise y elimine el proceso hijo una vez que se agote el tiempo de espera. Esto no creará un padre separado para monitorear a cada niño. También puede administrar todos los procesos secundarios correctamente dentro del mismo padre.
Cree test.sh de la siguiente manera,
#!/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;
y observe los procesos que tienen nombre como 'prueba' en otro terminal usando el siguiente comando.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
El script anterior creará 4 nuevos procesos secundarios. Estamos almacenando pids de todos los procesos secundarios y recorriéndolos para verificar si han terminado su ejecución o si aún se están ejecutando. El proceso hijo se ejecutará hasta el momento CMD_TIME. Pero si CNT_TIME_OUT alcanza el tiempo de espera, todos los niños serán asesinados por el proceso principal. Puede cambiar el tiempo y jugar con el script para ver el comportamiento. Un inconveniente de este enfoque es que está usando la identificación de grupo para matar todos los árboles secundarios. Pero el proceso padre en sí mismo pertenece al mismo grupo, por lo que también será asesinado.
Es posible que deba asignar otra identificación de grupo al proceso principal si no desea que se elimine el elemento principal.
#/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
oherodes
.Respuestas:
No dice si el árbol que desea matar es un solo grupo de procesos. (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 utilizando GNU ps de la siguiente manera:
Si es un grupo de proceso que desea eliminar, simplemente use el
kill(1)
comando pero en lugar de darle un número de proceso, dele la negación del número de grupo. Por ejemplo, para eliminar todos los procesos del grupo 5112, usekill -TERM -- -5112
.fuente
pgrep
puede ofrecer una manera más fácil de encontrar la ID del grupo de procesos. Por ejemplo, para matar el grupo de procesos my-script.sh, ejecutekill -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
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ñalKILL
(9)Puede recuperar el
PGID
desde cualquier ID de proceso (PID
) del mismo árbol de procesoskill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')
(señalTERM
)kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')
(señalKILL
)Un agradecimiento especial a tanager y Speakus por sus contribuciones en
$PID
los 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 la ID de grupo de proceso de cualquier ID de proceso del árbol, no solo la ID de padre de proceso . Una variación deps opgid= $PID
esps -o pgid --no-headers $PID
dondepgid
se puede reemplazar porpgrp
.Pero:
ps
inserta espacios iniciales cuandoPID
tiene menos de cinco dígitos y se alinea a la derecha como lo notó la tangara . Puedes usar:PGID=$(ps opgid= "$PID" | tr -d ' ')
ps
desde OSX siempre imprime el encabezado, por lo tanto, 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).Otras líneas de comando
Limitación
kill
es invocado por un proceso que pertenece al mismo árbol,kill
corre el riesgo de suicidarse antes de terminar con la destrucción del árbol completo.Larga historia
Ejecute el árbol de procesos en segundo plano usando '&'
El comando
pkill -P $PID
no mata al nieto:El comando
kill -- -$PGID
mata todos los procesos, incluido el nieto.Conclusión
Noto en este ejemplo
PID
yPGID
son iguales (28957
).Es por eso que originalmente pensé que
kill -- -$PID
era suficiente. Pero en el caso de que el proceso se desovar dentro de unMakefile
el ID de proceso es diferente del ID de grupo .Creo que
kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)
es el mejor truco simple para matar un árbol de proceso completo cuando se llama desde una ID de grupo diferente (otro árbol de proceso).fuente
kill
que siempre debería enviar la señal a todo el árbol antes de recibir su propia señal. Pero en algunas circunstancias / implementaciones específicas,kill
puede enviarse la señal a sí mismo, ser interrumpido y luego recibir su propia señal. Sin embargo, el riesgo debe ser lo suficientemente mínimo, y puede ignorarse en la mayoría de los casos porque deberían ocurrir otros errores antes de este. ¿Se puede ignorar este riesgo en su caso? Además, otras respuestas tienen este error común (kill
parte del árbol de proceso que se está eliminando). Espero que esta ayuda .. Saludos;)man
hacer eso. Por otro lado, si desea eliminar el proceso gandchild del proceso hijo,kill -- -$pid
no funcionará. Por lo tanto, no es una solución genérica.child.sh
.Esto eliminará todos los procesos que tengan el ID de proceso padre 27888.
O más robusto:
que programan la muerte 33 segundos después y cortésmente solicitan que finalicen los procesos.
Vea esta respuesta para terminar con todos los descendientes.
fuente
pkill -P
envía la señal solo al niño => el nieto no recibe la señal => Por lo tanto, he escrito otra respuesta para explicar eso. Saludos ;-)pkill -TERM -P ${$}
.$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
Para matar un árbol de proceso de forma recursiva, use killtree ():
fuente
--
argumentos paraps
no funcionar en OS X. Para hacerlo funcionar, reemplace elps
comando por:ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g
donde_regex
se define antes delfor
bucle:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
killtree()
funcione de manera confiable?ps
no es compatible--ppid
, se puede usarpgrep -P ${_pid}
en su lugarEl comando rkill del paquete pslist envía la señal dada (o
SIGTERM
por defecto) al proceso especificado y a todos sus descendientes:fuente
La respuesta de Brad es lo que yo también recomendaría, excepto que puede eliminarlo por
awk
completo si usa la--ppid
opciónps
.fuente
si sabes pasar el pid del proceso padre, aquí hay un script de shell que debería funcionar:
fuente
Utilizo una versión un poco modificada de un método descrito aquí: https://stackoverflow.com/a/5311362/563175
Entonces se ve así:
donde 24901 es el PID de los padres.
Se ve bastante feo pero hace su trabajo perfectamente.
fuente
pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
-l
apstree
, para que las líneas largas no se trunqueen se puede hacer más fácil de leer también conkill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`
(no es necesario convertir\n
al espacio ya que funcionará bien), gracias!Versión modificada de la respuesta de zhigang:
fuente
wait $pid
solo puede en procesos que ha comenzado, no en todos los precesos, por lo que esta no es una solución genéricawait
suprimirá elTerminated
mensaje si es un niño.No puedo comentar (no hay suficiente reputación), por lo que me veo obligado a agregar una nueva respuesta , aunque esta no sea realmente una respuesta.
Hay un pequeño problema con la respuesta por lo demás muy agradable y exhaustiva dada por @olibre el 28 de febrero. La salida de
ps opgid= $PID
contendrá espacios iniciales para un PID de menos de cinco dígitos porqueps
está justificando la columna (alinee los números). Dentro de la línea de comando completa, esto da como resultado un signo negativo, seguido de espacio (s), seguido del PID del grupo. Una solución simple es a la tuberíaps
atr
los espacios eliminar los siguientes:fuente
Para agregar a la respuesta de Norman Ramsey, puede valer la pena mirar setsid si desea crear un grupo de procesos.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html
Lo que quiero decir es que puedes crear un grupo desde el proceso de inicio. Usé esto en php para poder matar un árbol de proceso completo después de iniciarlo.
Esta puede ser una mala idea. Me interesarían los comentarios.
fuente
Inspirado por el comentario de ysth
fuente
PGID
a partirPID
. ¿Qué piensas? SaludosLa siguiente función de shell es similar a muchas de las otras respuestas, pero funciona tanto en Linux como en BSD (OS X, etc.) sin dependencias externas como
pgrep
:fuente
$child
esa función para no perturbar otras variables (no locales) con el mismo nombre y garantizar que el valor de la variable local se limpie después de que finalice la función.Es muy fácil hacer esto con python usando psutil . Simplemente instale psutil con pip y luego tendrá un conjunto completo de herramientas de manipulación de procesos:
fuente
Basado en la respuesta de zhigang, esto evita el auto-asesinato:
fuente
Si quieres matar un proceso por nombre:
o
fuente
Esta es mi versión de matar todos los procesos secundarios usando el script bash. No usa recursividad y depende del comando pgrep.
Utilizar
Contenido de killtrees.sh
fuente
Aquí hay una variación de la respuesta de @ zhigang que funciona sin AWK, confiando solo en las posibilidades de análisis nativo de Bash:
Parece funcionar bien tanto en Mac como en Linux. En situaciones en las que no puede confiar en la capacidad de administrar grupos de procesos, como cuando escribe scripts para probar un software que debe construirse en múltiples entornos, esta técnica de caminar por los árboles es definitivamente útil.
fuente
Gracias por su sabiduría, amigos. Mi script dejaba algunos procesos secundarios al salir y el consejo de negación facilitó las cosas. Escribí esta función para usarla en otros scripts si es necesario:
Salud.
fuente
Si tiene pstree y perl en su sistema, puede intentar esto:
fuente
Probablemente sea mejor matar al padre antes que a los hijos; de lo contrario, es probable que el padre genere nuevos hijos antes de que él mismo se mate. Estos sobrevivirán a la matanza.
Mi versión de ps es diferente de la anterior; tal vez demasiado viejo, por lo tanto, el extraño grepping ...
Usar un script de shell en lugar de una función de shell tiene muchas ventajas ...
Sin embargo, es básicamente idea zhigangs
fuente
Lo siguiente ha sido probado en FreeBSD, Linux y MacOS X y solo depende de pgrep y kill (las versiones ps -o no funcionan bajo BSD). El primer argumento es el padre pid de que los hijos tienen que ser terminados. El segundo argumento es un valor booleano para determinar si el pid padre también debe terminarse.
Esto enviará SIGTERM a cualquier proceso hijo / nieto dentro de un script de shell y si SIGTERM no tiene éxito, esperará 10 segundos y luego enviará kill.
Respuesta anterior:
Lo siguiente también funciona, pero matará el propio shell en BSD.
fuente
Desarrollo aún más la solución de zhigang, xyuri y solidsneck:
Esta versión evitará matar a sus ancestros, lo que provoca una avalancha de procesos secundarios en las soluciones anteriores.
Los procesos se detienen correctamente antes de determinar la lista secundaria, para que no se creen o desaparezcan nuevos elementos secundarios.
Después de ser asesinados, los trabajos detenidos deben continuar para desaparecer del sistema.
fuente
Antigua pregunta, lo sé, pero todas las respuestas parecen seguir llamando ps, lo que no me gustó.
Esta solución basada en awk no requiere recursividad y solo llama ps una vez.
O en una sola línea:
Básicamente, la idea es que construyamos una matriz (a) de entradas padre: hijo, luego recorramos la matriz buscando hijos para nuestros padres coincidentes, agregándolos a nuestra lista de padres (p) a medida que avanzamos.
Si no quieres matar el proceso de nivel superior, entonces haz
justo antes de que la línea system () lo elimine del conjunto de eliminación.
Tenga en cuenta que hay una condición de carrera aquí, pero eso es cierto (hasta donde puedo ver) de todas las soluciones. Hace lo que necesitaba porque el script para el que lo necesitaba no crea muchos niños de corta duración.
Un ejercicio para el lector sería convertirlo en un ciclo de 2 pasadas: después de la primera pasada, envíe SIGSTOP a todos los procesos en la lista p, luego realice un ciclo para ejecutar ps nuevamente y después de la segunda pasada envíe SIGTERM, luego SIGCONT. Si no te interesan los finales agradables, supongo que el segundo pase podría ser SIGKILL.
fuente
Si conoce el pid de lo que desea matar, generalmente puede ir desde la identificación de la sesión y todo en la misma sesión. Verificaría dos veces, pero usé esto para scripts que inician rsyncs en bucles que quiero morir, y no iniciar otro (debido al bucle) como lo haría si acabara de matar a rsync.
Si no conoces el pid, aún puedes anidar más
fuente
fuente
kill -9
, de verdad.kill -15
no ayuda.En sh, el comando jobs enumerará los procesos en segundo plano. En algunos casos, podría ser mejor eliminar primero el proceso más nuevo, por ejemplo, el anterior creó un socket compartido. En esos casos, clasifique los PID en orden inverso. A veces quieres esperar un momento para que los trabajos escriban algo en el disco o cosas así antes de que se detengan.
¡Y no mates si no tienes que hacerlo!
fuente
Proceso secundario de matar en script de shell:
Muchas veces necesitamos matar procesos secundarios que se cuelgan o bloquean por alguna razón. p.ej. Problema de conexión FTP.
Hay dos enfoques,
1) Crear un nuevo padre separado para cada hijo que supervisará y eliminará el proceso hijo una vez que se agote el tiempo de espera.
Cree test.sh de la siguiente manera,
y observe los procesos que tienen nombre como 'prueba' en otro terminal usando el siguiente comando.
El script anterior creará 4 nuevos procesos secundarios y sus padres. Cada proceso secundario se ejecutará durante 10 segundos. Pero una vez que se agota el tiempo de espera de 5 segundos, sus respectivos procesos principales matarán a esos niños. Por lo tanto, el niño no podrá completar la ejecución (10 segundos). Juega alrededor de esos tiempos (interruptor 10 y 5) para ver otro comportamiento. En ese caso, el hijo finalizará la ejecución en 5 segundos antes de que alcance el tiempo de espera de 10 segundos.
2) Deje que el padre actual supervise y elimine el proceso hijo una vez que se agote el tiempo de espera. Esto no creará un padre separado para monitorear a cada niño. También puede administrar todos los procesos secundarios correctamente dentro del mismo padre.
Cree test.sh de la siguiente manera,
y observe los procesos que tienen nombre como 'prueba' en otro terminal usando el siguiente comando.
El script anterior creará 4 nuevos procesos secundarios. Estamos almacenando pids de todos los procesos secundarios y recorriéndolos para verificar si han terminado su ejecución o si aún se están ejecutando. El proceso hijo se ejecutará hasta el momento CMD_TIME. Pero si CNT_TIME_OUT alcanza el tiempo de espera, todos los niños serán asesinados por el proceso principal. Puede cambiar el tiempo y jugar con el script para ver el comportamiento. Un inconveniente de este enfoque es que está usando la identificación de grupo para matar todos los árboles secundarios. Pero el proceso padre en sí mismo pertenece al mismo grupo, por lo que también será asesinado.
Es posible que deba asignar otra identificación de grupo al proceso principal si no desea que se elimine el elemento principal.
Más detalles se pueden encontrar aquí,
Proceso secundario de matar en script de shell
fuente
Este script también funciona:
#/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
fuente
Sé que es viejo, pero esa es la mejor solución que encontré:
fuente