Voglio uccidere un intero albero di processo. Qual è il modo migliore per farlo usando qualsiasi linguaggio di scripting comune? Sto cercando una soluzione semplice.
chronos
o herodes
.
Voglio uccidere un intero albero di processo. Qual è il modo migliore per farlo usando qualsiasi linguaggio di scripting comune? Sto cercando una soluzione semplice.
chronos
o herodes
.
Risposte:
Non dici se l'albero che vuoi uccidere è un singolo gruppo di processi. (Questo è spesso il caso se l'albero è il risultato di un fork dall'avvio di un server o da una riga di comando della shell.) Puoi scoprire i gruppi di processi usando GNU ps come segue:
ps x -o "%p %r %y %x %c "
Se si tratta di un gruppo di processi che si desidera eliminare, utilizzare semplicemente il kill(1)
comando ma invece di assegnargli un numero di processo, assegnare la negazione del numero di gruppo. Ad esempio, per interrompere tutti i processi nel gruppo 5112, utilizzare kill -TERM -- -5112
.
pgrep
può offrire un modo più semplice per trovare l'ID del gruppo di processi. Ad esempio, per uccidere il gruppo di processi di my-script.sh, esegui kill -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
Uccidere tutti i processi appartenenti allo stesso albero dei processi utilizzando l' ID gruppo di processi ( PGID
)
kill -- -$PGID
Usa segnale predefinito ( TERM
= 15)kill -9 -$PGID
Usa il segnale KILL
(9)È possibile recuperare PGID
da qualsiasi Process-ID ( PID
) dello stesso albero dei processi
kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')
(segnale TERM
)kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')
(segnale KILL
)Un ringraziamento speciale a tanager e Speakus per i contributi sugli $PID
spazi rimanenti e la compatibilità OSX.
kill -9 -"$PGID"
=> Invia il segnale 9 ( KILL
) a tutti i figli e nipoti ...PGID=$(ps opgid= "$PID")
=> Recupera l' ID gruppo di processo da qualsiasi ID processo dell'albero, non solo l' ID padre-processo . Una variante di ps opgid= $PID
è ps -o pgid --no-headers $PID
dove pgid
può essere sostituita da pgrp
. grep -o [0-9]*
stampa solo cifre successive (non stampa spazi o intestazioni alfabetiche).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)
kill
viene invocato da un processo appartenente allo stesso albero, kill
rischia di uccidersi prima di terminare l'uccisione dell'intero albero.> 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)"
Esegui l'albero dei processi in background 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
Il comando pkill -P $PID
non uccide il nipote:
> 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
Il comando kill -- -$PGID
uccide tutti i processi incluso il nipote.
> 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
Ho notato in questo esempio PID
e PGID
sono equal ( 28957
).
Questo è il motivo per cui inizialmente pensavo kill -- -$PID
fosse abbastanza. Ma nel caso il processo è depongono le uova all'interno di un Makefile
l' ID di processo è diverso dal ID gruppo .
Penso che kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)
sia il miglior trucco semplice per uccidere un intero albero di processo quando viene chiamato da un ID gruppo diverso (un altro albero di processo).
kill
dovrebbe sempre inviare il segnale all'intero albero prima di ricevere il proprio segnale. Ma in alcune circostanze / implementazioni specifiche, kill
può inviare a se stesso il segnale, essere interrotto e quindi ricevere il proprio segnale. Tuttavia, il rischio dovrebbe essere abbastanza minimo e potrebbe essere ignorato nella maggior parte dei casi perché altri bug dovrebbero verificarsi prima di questo. Questo rischio può essere ignorato nel tuo caso? Inoltre, altre risposte hanno questo bug comune ( kill
parte dell'albero del processo che viene ucciso). Spero che questo aiuto .. Saluti;)
man
farlo. D'altra parte, se vuoi uccidere il processo gandchild dal processo figlio, kill -- -$pid
non funzionerà. Quindi non è una soluzione generica.
child.sh
.
pkill -TERM -P 27888
Ciò ucciderà tutti i processi che hanno l'ID processo padre 27888.
O più robusto:
CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS
che pianifica l'uccisione di 33 secondi dopo e cortesemente chiede ai processi di terminare.
Vedi questa risposta per terminare tutti i discendenti.
pkill -P
invia il segnale solo al figlio => il nipote non riceve il segnale => Pertanto ho scritto un'altra risposta per spiegarlo. Saluti ;-)
pkill -TERM -P ${$}
.
$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
Per uccidere un albero di processo in modo ricorsivo, usa 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 $@
--
argomenti per ps
non funzionare su OS X. Per farlo funzionare, sostituisci il ps
comando con: ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g
dove _regex
è definito prima del for
ciclo:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
killtree()
modo affidabile?
ps
non supporta --ppid
, si può usare pgrep -P ${_pid}
invece
Anche la risposta di Brad è ciò che consiglierei, tranne per il fatto che puoi eliminare del awk
tutto se usi l' --ppid
opzione per ps
.
for child in $(ps -o pid -ax --ppid $PPID) do ....... done
se sai passare il pid del processo genitore, ecco uno script di shell che dovrebbe funzionare:
for child in $(ps -o pid,ppid -ax | \
awk "{ if ( \$2 == $pid ) { print \$1 }}")
do
echo "Killing child process $child because ppid = $pid"
kill $child
done
Uso una versione leggermente modificata di un metodo descritto qui: https://stackoverflow.com/a/5311362/563175
Quindi sembra che:
kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`
dove 24901 è il PID del genitore.
Sembra piuttosto brutto ma fa perfettamente il suo lavoro.
pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
-l
a pstree
, così le linee lunghe non vengono troncate; può essere reso più semplice da leggere anche con kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`
(non è necessario convertire \n
nello spazio perché funzionerà bene), grazie!
Versione modificata della risposta di zhigang:
#!/usr/bin/env bash
set -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 "
fi
done
}
while true; do sleep 1; done &
cpid=$!
for i in $(seq 1 2); do
cpids $$ a
sleep 1
done
killtree $cpid
echo ---
cpids $$ a
wait $pid
solo sui processi che hai avviato, non tutte le procedure preliminari, quindi questa non è una soluzione generica
wait
sopprimerà il Terminated
messaggio se è un bambino.
Non posso commentare (non abbastanza reputazione), quindi sono costretto ad aggiungere una nuova risposta , anche se questa non è davvero una risposta.
C'è un leggero problema con la risposta altrimenti molto bella e approfondita fornita da @olibre il 28 febbraio. L'output di ps opgid= $PID
conterrà spazi iniziali per un PID inferiore a cinque cifre perché ps
giustifica la colonna (allineare i numeri in modo rigoroso). All'interno dell'intera riga di comando, questo si traduce in un segno negativo, seguito da uno o più spazi, seguito dal gruppo PID. Soluzione semplice è quello di inviare ps
a tr
agli spazi rimuovere:
kill -- -$( ps opgid= $PID | tr -d ' ' )
Per aggiungere alla risposta di Norman Ramsey, potrebbe essere utile esaminare setsid se si desidera creare un gruppo di processi.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html
La funzione setsid () deve creare una nuova sessione, se il processo chiamante non è un leader del gruppo di processi. Al ritorno, il processo di chiamata deve essere il leader della sessione di questa nuova sessione, deve essere il leader del gruppo di processo di un nuovo gruppo di processo e non deve disporre di un terminale di controllo. L'ID del gruppo di processi del processo chiamante deve essere impostato uguale all'ID del processo chiamante. Il processo di chiamata deve essere l'unico processo nel nuovo gruppo di processi e l'unico processo nella nuova sessione.
Il che presumo significhi che è possibile creare un gruppo dal processo iniziale. L'ho usato in php per poter uccidere un intero albero di processo dopo averlo avviato.
Questa potrebbe essere una cattiva idea. Sarei interessato a commenti.
Ispirato dal commento di ysth
kill -- -PGID
invece di assegnargli un numero di processo, assegnagli la negazione del numero di gruppo. Come al solito con quasi tutti i comandi, se si desidera un argomento normale che inizia con un
-
non essere interpretato come un interruttore, precederlo con--
PGID
da PID
. Cosa ne pensi? Saluti
La seguente funzione di shell è simile a molte altre risposte, ma funziona sia su Linux che su BSD (OS X, ecc.) Senza dipendenze esterne come 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
}
$child
tale funzione in modo da non disturbare altre variabili (non locali) con lo stesso nome e garantire che il valore della variabile locale venga ripulito al termine della funzione.
È semplicissimo farlo con Python usando psutil . Installa psutil con pip e avrai una suite completa di strumenti di manipolazione dei processi:
def killChildren(pid):
parent = psutil.Process(pid)
for child in parent.get_children(True):
if child.is_running():
child.terminate()
Se vuoi terminare un processo per nome:
killall -9 -g someprocessname
o
pgrep someprocessname | xargs pkill -9 -g
Questa è la mia versione di uccidere tutti i processi figlio usando lo script bash. Non utilizza la ricorsione e dipende dal comando pgrep.
Uso
killtree.sh PID SIGNAL
Contenuto di killtrees.sh
#!/bin/bash
PID=$1
if [ -z $PID ];
then
echo "No pid specified"
fi
PPLIST=$PID
CHILD_LIST=`pgrep -P $PPLIST -d,`
while [ ! -z "$CHILD_LIST" ]
do
PPLIST="$PPLIST,$CHILD_LIST"
CHILD_LIST=`pgrep -P $CHILD_LIST -d,`
done
SIGNAL=$2
if [ -z $SIGNAL ]
then
SIGNAL="TERM"
fi
#do substring from comma to space
kill -$SIGNAL ${PPLIST//,/ }
Ecco una variante della risposta di @ zhigang che funziona senza AWK, basandosi solo sulle possibilità di analisi native di Bash:
function killtree {
kill -STOP "$1"
ps -e -o pid= -o ppid= | while read -r pid ppid
do
[[ $ppid = $1 ]] || continue
killtree "$pid" || true # Skip over failures
done
kill -CONT "$1"
kill -TERM "$1"
}
Sembra funzionare bene su Mac e Linux. In situazioni in cui non è possibile fare affidamento sulla capacità di gestire gruppi di processi, ad esempio quando si scrivono script per testare un software che deve essere costruito in più ambienti, questa tecnica di camminata sugli alberi è sicuramente utile.
Grazie per la tua saggezza, gente. La mia sceneggiatura stava lasciando alcuni processi figlio all'uscita e la punta della negazione ha reso le cose più facili. Ho scritto questa funzione da utilizzare in altri script se necessario:
# 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
}
Saluti.
Probabilmente è meglio uccidere il genitore prima dei figli; altrimenti il genitore potrebbe probabilmente generare di nuovo nuovi figli prima di essere ucciso. Questi sopravviveranno all'omicidio.
La mia versione di ps è diversa da quella sopra; forse troppo vecchio, quindi lo strano grepping ...
Utilizzare uno script di shell anziché una funzione di shell presenta molti vantaggi ...
Tuttavia, è fondamentalmente un'idea di zhigang
#!/bin/bash
if test $# -lt 1 ; then
echo >&2 "usage: kiltree pid (sig)"
fi ;
_pid=$1
_sig=${2:-TERM}
_children=$(ps j | grep "^[ ]*${_pid} " | cut -c 7-11) ;
echo >&2 kill -${_sig} ${_pid}
kill -${_sig} ${_pid}
for _child in ${_children}; do
killtree ${_child} ${_sig}
done
Quanto segue è stato testato su FreeBSD, Linux e MacOS X e dipende solo da pgrep e kill (le versioni ps -o non funzionano con BSD). Il primo argomento è il pid genitore di cui devono essere terminati i figli. il secondo argomento è un valore booleano per determinare se anche il pid padre deve essere terminato.
KillChilds() {
local pid="${1}"
local self="${2:-false}"
if children="$(pgrep -P "$pid")"; then
for child in $children; do
KillChilds "$child" true
done
fi
if [ "$self" == true ]; then
kill -s SIGTERM "$pid" || (sleep 10 && kill -9 "$pid" &)
fi
}
KillChilds $$ > /dev/null 2>&1
Questo invierà SIGTERM a qualsiasi processo figlio / nipote all'interno di uno script di shell e se SIGTERM non riesce, attenderà 10 secondi e quindi invierà kill.
Prima risposta:
Di seguito funziona anche ma ucciderà la shell stessa su BSD.
KillSubTree() {
local parent="${1}"
for child in $(ps -o pid=$parent); do
if [ $$ -ne $child ]; then (kill -s SIGTERM $child || (sleep 10 && kill -9 $child & )) > /dev/null 2>&1 ; fi
done
}
# Example lanch from within script
KillSubTree $$ > /dev/null 2>&1
Sviluppo ulteriormente la soluzione di zhigang, xyuri e solidsneck:
#!/bin/bash
if 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." ;
return 1 ;
fi ;
# this avoids that children are spawned or disappear.
kill -SIGSTOP $2 ;
_children=$(ps -o pid --no-headers --ppid $2) ;
_success=0
for _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
Questa versione eviterà di uccidere i suoi antenati, il che provoca un'inondazione di processi figlio nelle soluzioni precedenti.
I processi vengono arrestati correttamente prima di determinare l'elenco figlio, in modo che non vengano creati o scompaiano nuovi figli.
Dopo essere stati uccisi, i lavori interrotti devono continuare a scomparire dal sistema.
Vecchia domanda, lo so, ma tutte le risposte sembrano continuare a chiamare ps, cosa che non mi è piaciuta.
Questa soluzione basata su awk non richiede ricorsione e chiama ps solo una volta.
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 su una riga singola:
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)}'
Fondamentalmente l'idea è che costruiamo un array (a) di parent: voci figlio, quindi giriamo attorno all'array alla ricerca di figli per i nostri genitori corrispondenti, aggiungendoli all'elenco dei genitori (p) mentre procediamo.
Se non vuoi uccidere il processo di livello superiore, allora fallo
sub(/[0-9]*/, "", p)
appena prima che la riga system () lo rimuovesse dal kill set.
Tieni presente che qui c'è una condizione di razza, ma è vero (per quanto posso vedere) di tutte le soluzioni. Fa quello che mi serviva perché la sceneggiatura di cui avevo bisogno non creava molti bambini di breve durata.
Un esercizio per il lettore sarebbe quello di renderlo un ciclo a 2 passaggi: dopo il primo passaggio, inviare SIGSTOP a tutti i processi nell'elenco p, quindi eseguire il ciclo per eseguire nuovamente ps e dopo il secondo passaggio inviare SIGTERM, quindi SIGCONT. Se non ti interessano i bei finali, il secondo passaggio potrebbe essere SIGKILL, suppongo.
Se conosci il pid della cosa che vuoi uccidere, di solito puoi passare dall'ID sessione e tutto nella stessa sessione. Avrei ricontrollato, ma l'ho usato per gli script che avviano rsync in loop che voglio morire, e non ne inizio un altro (a causa del loop) come farebbe se uccidessi rsync.
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))
Se non conosci il pid puoi comunque nidificare di più
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))
ps -o pid= --ppid $PPID | xargs kill -9
kill -9
davvero.
kill -15
non aiuta.
In sh il comando jobs elencherà i processi in background. In alcuni casi potrebbe essere meglio interrompere prima il processo più recente, ad esempio quello precedente ha creato un socket condiviso. In questi casi, ordinare i PID in ordine inverso. A volte vuoi aspettare un momento prima che i lavori scrivano qualcosa sul disco o cose del genere prima che si fermino.
E non uccidere se non è necessario!
for SIGNAL in TERM KILL; do
for CHILD in $(jobs -s|sort -r); do
kill -s $SIGNAL $CHILD
sleep $MOMENT
done
done
Uccidere il processo figlio nello script della shell:
Molte volte dobbiamo uccidere il processo figlio che viene impiccato o bloccato per qualche motivo. per esempio. Problema di connessione FTP.
Ci sono due approcci,
1) Creare un nuovo genitore separato per ogni bambino che controllerà e ucciderà il processo figlio una volta raggiunto il timeout.
Crea test.sh come segue,
#!/bin/bash
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
for CMD in ${CMDs[*]}; do
(sleep 10 & PID=$!; echo "Started $CMD => $PID"; sleep 5; echo "Killing $CMD => $PID"; kill $PID; echo "$CMD Completed.") &
done
exit;
e guarda i processi che hanno il nome come 'test' in altri terminali usando il seguente comando.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Lo script sopra creerà 4 nuovi processi figlio e i loro genitori. Ogni processo figlio verrà eseguito per 10 secondi. Ma una volta scaduto il tempo di 5 secondi, i rispettivi processi genitore uccideranno quei bambini. Quindi il bambino non sarà in grado di completare l'esecuzione (10 sec). Gioca attorno a quei tempi (interruttore 10 e 5) per vedere un altro comportamento. In tal caso, il bambino termina l'esecuzione in 5 secondi prima che raggiunga il timeout di 10 secondi.
2) Consentire al genitore corrente di monitorare e terminare il processo figlio una volta raggiunto il timeout. Questo non creerà un genitore separato per monitorare ogni bambino. Inoltre puoi gestire correttamente tutti i processi figlio all'interno dello stesso genitore.
Crea test.sh come segue,
#!/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.";
fi
done
if [ ${#TMP_CPIDs[@]} == 0 ]; then
echo "All commands completed.";
break;
else
unset CPIDs;
declare -A CPIDs;
for PID in "${!TMP_CPIDs[@]}"; do
CPIDs[$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;
fi
fi
CNT=$((CNT+1));
echo "waiting since $b secs..";
sleep 1;
done
exit;
e guarda i processi che hanno il nome come 'test' in altri terminali usando il seguente comando.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Lo script sopra creerà 4 nuovi processi figlio. Stiamo memorizzando i pid di tutto il processo figlio e li eseguiamo in loop per verificare se hanno terminato la loro esecuzione o se sono ancora in esecuzione. Il processo figlio verrà eseguito fino all'ora CMD_TIME. Ma se CNT_TIME_OUT raggiunge il timeout, tutti i bambini verranno uccisi dal processo padre. Puoi cambiare i tempi e giocare con lo script per vedere il comportamento. Uno svantaggio di questo approccio è l'uso dell'ID di gruppo per uccidere tutto l'albero figlio. Ma il processo genitore stesso appartiene allo stesso gruppo, quindi verrà anche ucciso.
Potrebbe essere necessario assegnare un altro ID gruppo al processo genitore se non si desidera che il genitore venga ucciso.
Maggiori dettagli possono essere trovati qui,
Questo script funziona anche:
#/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
So che è vecchio, ma questa è la soluzione migliore che ho trovato:
killtree() {
for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
echo Terminating: $p
kill $p
done
}