Chcę zabić całe drzewo procesów. Jaki jest najlepszy sposób, aby to zrobić przy użyciu popularnych języków skryptowych? Szukam prostego rozwiązania.
chronos
lub herodes
.
Chcę zabić całe drzewo procesów. Jaki jest najlepszy sposób, aby to zrobić przy użyciu popularnych języków skryptowych? Szukam prostego rozwiązania.
chronos
lub herodes
.
Odpowiedzi:
Nie mówisz, że drzewo, które chcesz zabić, jest pojedynczą grupą procesów. (Często dzieje się tak, gdy drzewo powstaje w wyniku uruchamiania serwera lub wiersza poleceń powłoki.) Grupy procesów można odkryć za pomocą GNU ps w następujący sposób:
ps x -o "%p %r %y %x %c "
Jeśli jest to grupa procesów, którą chcesz zabić, po prostu użyj kill(1)
polecenia, ale zamiast nadać jej numer procesu, podaj negację numeru grupy. Na przykład, aby zabić każdy proces w grupie 5112, użyj kill -TERM -- -5112
.
pgrep
może zaoferować łatwiejszy sposób znalezienia identyfikatora grupy procesów. Na przykład, aby zabić grupę procesów my-script.sh, uruchom 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
Zabij wszystkie procesy należące do tego samego drzewa procesów za pomocą ID grupy procesów ( PGID
)
kill -- -$PGID
Użyj domyślnego sygnału ( TERM
= 15)kill -9 -$PGID
Użyj sygnału KILL
(9)Możesz pobrać PGID
z dowolnego ID procesu ( PID
) tego samego drzewa procesów
kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')
(sygnał TERM
)kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')
(sygnał KILL
)Specjalne podziękowania dla tanager i Speakus za wkład w $PID
pozostałe miejsca i kompatybilność z OSX.
kill -9 -"$PGID"
=> Wyślij sygnał 9 ( KILL
) do wszystkich dzieci i wnuków ...PGID=$(ps opgid= "$PID")
=> Pobierz identyfikator grupy procesów z dowolnego identyfikatora procesu drzewa, nie tylko z identyfikatora procesu rodzica . Odmianą ps opgid= $PID
jest miejsce, w ps -o pgid --no-headers $PID
którym pgid
można je zastąpić pgrp
. grep -o [0-9]*
drukuje tylko kolejne cyfry (nie drukuje spacji ani nagłówków alfabetycznych).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
wywoływana jest przez proces należącego do tego samego drzewa, kill
ryzykuje się zabić przed zakończeniem całego drzewa zabijania.> 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)"
Uruchom drzewo procesów w tle za pomocą „&”
> ./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
Polecenie pkill -P $PID
nie zabija wnuka:
> 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
Komenda kill -- -$PGID
zabija wszystkie procesy, w tym wnuka.
> 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
Zauważam w tym przykładzie PID
i PGID
są równe ( 28957
).
Właśnie dlatego początkowo myślałem, że kill -- -$PID
wystarczy. Ale w przypadku, gdy proces jest spawnowany w Makefile
ramach identyfikatora procesu, różni się on od identyfikatora grupy .
Myślę, że kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)
to najlepsza prosta sztuczka polegająca na zabiciu całego drzewa procesów, gdy jest wywoływana z innego identyfikatora grupy (innego drzewa procesów).
kill
zawsze powinien wysyłać sygnał do całego drzewa przed otrzymaniem własnego sygnału. Ale w niektórych szczególnych okolicznościach / implementacjach kill
może wysłać sobie sygnał, zostać przerwany, a następnie odebrać własny sygnał. Jednak ryzyko powinno być wystarczająco minimalne i może być ignorowane w większości przypadków, ponieważ przed tym powinny wystąpić inne błędy. Czy to ryzyko można zignorować w twoim przypadku? Ponadto w innych odpowiedziach występuje ten wspólny błąd ( kill
część drzewa procesu jest zabijana). Mam nadzieję, że ta pomoc .. Pozdrawiam;)
man
tego typu. Z drugiej strony, jeśli chcesz zabić proces gandchilda z procesu potomnego, kill -- -$pid
nie zadziała. Więc to nie jest ogólne rozwiązanie.
child.sh
.
pkill -TERM -P 27888
Spowoduje to zabicie wszystkich procesów, które mają identyfikator procesu nadrzędnego 27888.
Lub bardziej solidny:
CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS
który planuje zabić 33 sekundy później i uprzejmie prosi procesy o zakończenie.
Zobacz tę odpowiedź na zakończenie wszystkich potomków.
pkill -P
wysyła sygnał tylko do dziecka => wnuk nie odbiera sygnału => Dlatego mam wroten inną odpowiedź, aby to wyjaśnić. Pozdrawiam ;-)
pkill -TERM -P ${$}
.
$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
Aby rekurencyjnie zabić drzewo procesów, użyj 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 $@
--
argumenty ps
nie działają na OS X, aby pracować tam zastąpić ps
komendę: ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g
gdzie _regex
jest zdefiniowana przed for
pętli:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
killtree()
działa niezawodnie?
ps
nie obsługuje --ppid
, można pgrep -P ${_pid}
zamiast tego użyć
Brad też jest tym, co poleciłbym, z tym wyjątkiem, że możesz awk
całkowicie zrezygnować, jeśli skorzystasz z --ppid
opcji ps
.
for child in $(ps -o pid -ax --ppid $PPID) do ....... done
jeśli wiesz, że przekazałeś pid procesu nadrzędnego, oto skrypt powłoki, który powinien działać:
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
Używam nieco zmodyfikowanej wersji metody opisanej tutaj: https://stackoverflow.com/a/5311362/563175
Wygląda to tak:
kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`
gdzie 24901 jest PID rodzica.
Wygląda dość brzydko, ale doskonale spełnia swoją funkcję.
pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
-l
do pstree
tak długie linie nie obcięta; można go uprościć również czytając kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`
(nie trzeba konwertować \n
do miejsca, ponieważ będzie działać dobrze), dzięki!
Zmodyfikowana wersja odpowiedzi zhiganga:
#!/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
tylko na rozpoczętych procesach, nie na wszystkich precesjach, więc nie jest to ogólne rozwiązanie
wait
ukryje Terminated
wiadomość, jeśli jest to dziecko.
Nie mogę komentować (za mało reputacji), więc jestem zmuszony dodać nową odpowiedź , nawet jeśli tak naprawdę nie jest to odpowiedź.
Istnieje niewielki problem z bardzo ładną i dokładną odpowiedzią udzieloną przez @olibre 28 lutego. Wyjście ps opgid= $PID
będzie zawierało spacje wiodące dla PID krótszego niż pięć cyfr, ponieważ ps
uzasadnia to kolumnę (dokładnie wyrównaj liczby). W całym wierszu polecenia powoduje to znak ujemny, następnie spacje, a następnie PID grupy. Prostym rozwiązaniem jest usunięcie potoku ps
z rurki tr
:
kill -- -$( ps opgid= $PID | tr -d ' ' )
Aby dodać do odpowiedzi Normana Ramseya, warto spojrzeć na setsid, jeśli chcesz utworzyć grupę procesów.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html
Funkcja setsid () utworzy nową sesję, jeśli proces wywołujący nie jest liderem grupy procesów. Po powrocie proces wywołujący będzie liderem sesji nowej sesji, będzie liderem grupy procesów nowej grupy procesów i nie będzie miał terminala sterującego. Identyfikator grupy procesów procesu wywołującego powinien być równy identyfikatorowi procesu wywołującego. Proces wywoływania będzie jedynym procesem w nowej grupie procesów i jedynym procesem w nowej sesji.
Co rozumiem przez to, że możesz utworzyć grupę od samego początku. Użyłem tego w php, aby móc zabić całe drzewo procesów po jego uruchomieniu.
To może być zły pomysł. Byłbym zainteresowany komentarzami.
Zainspirowany komentarzem ysth
kill -- -PGID
zamiast nadawać mu numer procesu, podaj mu negację numeru grupy. Jak zwykle w prawie każdym poleceniu, jeśli chcesz, aby zwykły argument zaczynający się od a
-
nie był interpretowany jako przełącznik, poprzedz go--
PGID
od PID
. Co myślisz? Pozdrawiam
Poniższa funkcja powłoki jest podobna do wielu innych odpowiedzi, ale działa zarówno w systemie Linux, jak i BSD (OS X itp.) Bez zewnętrznych zależności, takich jak 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
tej funkcji, aby nie zakłócać innych (nielokalnych) zmiennych o tej samej nazwie i zapewnić, że wartość zmiennej lokalnej zostanie wyczyszczona po zakończeniu funkcji.
Bardzo łatwo jest to zrobić za pomocą Pythona za pomocą psutil . Wystarczy zainstalować psutil z pipem, a następnie masz pełny zestaw narzędzi do manipulacji procesami:
def killChildren(pid):
parent = psutil.Process(pid)
for child in parent.get_children(True):
if child.is_running():
child.terminate()
Jeśli chcesz zabić proces według nazwy:
killall -9 -g someprocessname
lub
pgrep someprocessname | xargs pkill -9 -g
To jest moja wersja zabijania wszystkich procesów potomnych za pomocą skryptu bash. Nie używa rekurencji i zależy od polecenia pgrep.
Posługiwać się
killtree.sh PID SIGNAL
Zawartość 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//,/ }
Oto odmiana odpowiedzi @ zhigang, która działa bez AWK, opierając się tylko na natywnych możliwościach parsowania Basha:
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"
}
Wygląda na to, że działa dobrze zarówno na komputerach Mac, jak i Linux. W sytuacjach, w których nie można polegać na możliwości zarządzania grupami procesów - na przykład podczas pisania skryptów do testowania oprogramowania, które musi być zbudowane w wielu środowiskach - ta technika chodzenia po drzewie jest zdecydowanie pomocna.
Dzięki za mądrość, ludzie. Mój skrypt pozostawiał niektóre procesy potomne przy wyjściu, a wskazówka negacji ułatwiła wszystko. Napisałem tę funkcję do użycia w innych skryptach, jeśli to konieczne:
# 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
}
Twoje zdrowie.
Prawdopodobnie lepiej zabić rodzica przed dziećmi; w przeciwnym razie rodzic prawdopodobnie ponownie odrodzi nowe dzieci, zanim sam zostanie zabity. Przetrwają zabijanie.
Moja wersja ps różni się od powyższej; może za stary, dlatego dziwne grep ...
Używanie skryptu powłoki zamiast funkcji powłoki ma wiele zalet ...
Jest to jednak w zasadzie pomysł 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
Poniższe testy zostały przetestowane na FreeBSD, Linux i MacOS X i zależą tylko od pgrep i kill (wersje ps -o nie działają pod BSD). Pierwszym argumentem jest pid nadrzędny, którego dzieci muszą zostać zakończone. drugi argument to wartość logiczna określająca, czy nadrzędny pid również musi zostać zakończony.
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
Spowoduje to wysłanie SIGTERM do dowolnego procesu potomnego / wnuka w skrypcie powłoki, a jeśli SIGTERM się nie powiedzie, poczeka 10 sekund, a następnie wyśle kill.
Wcześniejsza odpowiedź:
Poniższe działa również, ale zabije samą powłokę na 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
Rozwijam dalej rozwiązania zhigang, xyuri i 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
Ta wersja pozwoli uniknąć zabijania jego przodków - co powoduje zalew procesów potomnych w poprzednich rozwiązaniach.
Procesy są odpowiednio zatrzymywane przed ustaleniem listy potomnej, aby żadne nowe elementy potomne nie zostały utworzone ani zniknęły.
Po zabiciu zatrzymane zadania muszą nadal znikać z systemu.
Stare pytanie, wiem, ale wszystkie odpowiedzi wydają się wciąż nazywać ps, co mi się nie podobało.
To rozwiązanie oparte na awk nie wymaga rekurencji i wywołuje ps tylko raz.
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)
}'
Lub w jednym wierszu:
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)}'
Zasadniczo chodzi o to, że tworzymy tablicę (a) elementu nadrzędnego: wpisy podrzędne, a następnie zapętlamy tablicę, szukając dzieci dla naszych pasujących rodziców, dodając je do naszej listy nadrzędnej (p).
Jeśli nie chcesz zabić procesu najwyższego poziomu, to rób
sub(/[0-9]*/, "", p)
tuż przed tym, jak linia system () usunie go z zestawu zabić.
Pamiętaj, że jest tutaj warunek wyścigu, ale jest to prawda (o ile widzę) wszystkich rozwiązań. Robi to, czego potrzebowałem, ponieważ skrypt, dla którego go potrzebowałem, nie tworzy wielu krótkotrwałych dzieci.
Ćwiczeniem dla czytelnika byłoby uczynienie z niego pętli 2-przebiegowej: po pierwszym przejściu wyślij SIGSTOP do wszystkich procesów na liście p, następnie zapętl, aby ponownie uruchomić ps, a po drugim przejściu wyślij SIGTERM, a następnie SIGCONT. Jeśli nie zależy ci na ładnych zakończeniach, drugie przejście może być po prostu SIGKILL.
Jeśli znasz pid rzeczy, którą chcesz zabić, zwykle możesz przejść z identyfikatora sesji i wszystkiego w tej samej sesji. Sprawdziłbym dwa razy, ale użyłem tego do skryptów uruchamiających rsyncs w pętlach, które chcę umrzeć, a nie uruchamiających kolejnych (z powodu pętli), tak jak wtedy, gdybym po prostu zabił wszystkie rsync.
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))
Jeśli nie znasz pid, możesz zagnieżdżać więcej
kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))
ps -o pid= --ppid $PPID | xargs kill -9
kill -9
, naprawdę.
kill -15
nie pomoże.
W sh polecenie zadań wyświetli listę procesów w tle. W niektórych przypadkach może być lepiej zabić najpierw najnowszy proces, np. Starszy stworzył wspólne gniazdo. W takich przypadkach posortuj PID w odwrotnej kolejności. Czasami chcesz poczekać, aż zadania zapiszą coś na dysku lub coś takiego, zanim przestaną.
I nie zabijaj, jeśli nie musisz!
for SIGNAL in TERM KILL; do
for CHILD in $(jobs -s|sort -r); do
kill -s $SIGNAL $CHILD
sleep $MOMENT
done
done
Zabijanie procesu potomnego w skrypcie powłoki:
Wiele razy musimy zabić proces potomny, który z jakiegoś powodu został powieszony lub zablokowany. na przykład. Problem z połączeniem FTP.
Istnieją dwa podejścia,
1) Aby utworzyć osobnego nowego rodzica dla każdego dziecka, które będzie monitorować i zabijać proces potomny, gdy upłynie limit czasu.
Utwórz test.sh w następujący sposób,
#!/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;
i obserwuj procesy o nazwie „test” w innym terminalu za pomocą następującego polecenia.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Powyższy skrypt utworzy 4 nowe procesy potomne i ich rodziców. Każdy proces potomny będzie działał przez 10 sekund. Ale gdy osiągnie limit 5 sekund, odpowiednie procesy nadrzędne zabiją te dzieci. Dlatego dziecko nie będzie w stanie ukończyć wykonywania (10 sekund). Rozegraj te czasy (przełączniki 10 i 5), aby zobaczyć inne zachowanie. W takim przypadku dziecko zakończy wykonywanie w 5 sekund, zanim osiągnie limit 10 sekund.
2) Pozwól, aby bieżący rodzic monitorował i zabił proces potomny, gdy upłynie limit czasu. To nie stworzy osobnego rodzica do monitorowania każdego dziecka. Ponadto możesz poprawnie zarządzać wszystkimi procesami potomnymi w obrębie tego samego rodzica.
Utwórz test.sh w następujący sposób,
#!/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;
i obserwuj procesy o nazwie „test” w innym terminalu za pomocą następującego polecenia.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
Powyższy skrypt utworzy 4 nowe procesy potomne. Przechowujemy pids wszystkich procesów potomnych i zapętlamy je, aby sprawdzić, czy zakończyły się ich wykonywanie, czy nadal działają. Proces potomny będzie wykonywany do czasu CMD_TIME. Ale jeśli przekroczony zostanie limit czasu CNT_TIME_OUT, wszystkie dzieci zostaną zabite przez proces nadrzędny. Możesz zmienić czas i bawić się skryptem, aby zobaczyć zachowanie. Wadą tego podejścia jest użycie identyfikatora grupy do zabicia całego drzewa potomnego. Ale sam proces macierzysty należy do tej samej grupy, więc również zostanie zabity.
Może być konieczne przypisanie innego identyfikatora grupy procesowi nadrzędnemu, jeśli nie chcesz, aby rodzic został zabity.
Więcej informacji można znaleźć tutaj,
Ten skrypt działa również:
#/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
Wiem, że to stare, ale to lepsze rozwiązanie, które znalazłem:
killtree() {
for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
echo Terminating: $p
kill $p
done
}