@ MichaelLeBarbierGrünewald Czy możesz link do tych programów?
mucha styropianowa
Odpowiedzi:
305
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.
kill -74313 -bash: kill: 74313: niepoprawna specyfikacja sygnału Jeśli dodam kill -15 -GPID, to zadziała idealnie.
Adam Peck,
51
Jak zwykle w prawie każdym poleceniu, jeśli chcesz, aby normalny argument, który zaczyna się od - nie był interpretowany jako przełącznik, poprzedź go: -: kill - -GPID
ysth
9
pgrepmoż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).
Josh Kelley,
9
Lepiej spójrz na stackoverflow.com/questions/392022/… jest to zdecydowanie bardziej eleganckie rozwiązanie, a jeśli chcesz wymienić pids dzieci, użyj:ps -o pid --no-headers --ppid $PARENT_PID
Szymon Jeż
4
A jeśli nieznacznie zmodyfikujesz format i posortujesz, zobaczysz wszystkie procesy ładnie pogrupowane i zaczynając od (potencjalnie) rodzica grupy w każdej grupie:ps x -o "%r %p %y %x %c" | sort -nk1,2
haridsv
199
Zabij wszystkie procesy należące do tego samego drzewa procesów za pomocą ID grupy procesów ( PGID)
Specjalne podziękowania dla tanager i Speakus za wkład w $PIDpozostałe miejsca i kompatybilność z OSX.
Wyjaśnienie
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= $PIDjest miejsce, w ps -o pgid --no-headers $PIDktórym pgidmożna je zastąpić pgrp. Ale:
pswstawia spacje wiodące, gdy PIDma mniej niż pięć cyfr i jest wyrównany do prawej, jak zauważył tanager . Możesz użyć: PGID=$(ps opgid= "$PID" | tr -d ' ')
grep -o [0-9]* drukuje tylko kolejne cyfry (nie drukuje spacji ani nagłówków alfabetycznych).
Dalsze wiersze poleceń
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)
Ograniczenie
Jak zauważył przez Davide i Hubert Kario , gdy killwywoływana jest przez proces należącego do tego samego drzewa, killryzykuje się zabić przed zakończeniem całego drzewa zabijania.
Dlatego należy uruchomić polecenie przy użyciu procesu o innym identyfikatorze grupy procesów .
>./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
Polecenie pkill -P $PIDnie zabija wnuka:
> 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
Komenda kill -- -$PGIDzabija 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
28348283492834928349 pts/329039Ss330210:00-bash
28349290392903928349 pts/329039 R+330210:00 \_ ps fj
Wniosek
Zauważam w tym przykładzie PIDi PGIDsą równe ( 28957).
Właśnie dlatego początkowo myślałem, że kill -- -$PIDwystarczy. Ale w przypadku, gdy proces jest spawnowany w Makefileramach 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).
Cześć @davide. Dobre pytanie. Myślę, że killzawsze powinien wysyłać sygnał do całego drzewa przed otrzymaniem własnego sygnału. Ale w niektórych szczególnych okolicznościach / implementacjach killmoż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 ( killczęść drzewa procesu jest zabijana). Mam nadzieję, że ta pomoc .. Pozdrawiam;)
olibre
3
Działa to tylko wtedy, gdy same polecenia podrzędne nie stają się liderami grup. Nawet takie proste narzędzia mantego typu. Z drugiej strony, jeśli chcesz zabić proces gandchilda z procesu potomnego, kill -- -$pidnie zadziała. Więc to nie jest ogólne rozwiązanie.
Hubert Kario,
1
Przykładem jest „dziecko” próbujące zabić swoje dzieci (czyli wnuki polecenia zainicjowanego przez użytkownika). IOW, spróbuj zabić hierarchię procesów w tle w child.sh.
Hubert Kario,
1
> kill -QUIT - "$ PGID" # taki sam sygnał jak [CRTL + C] z klawiatury ---- Aby
zakończyć, należy WYJŚĆ
1
dla opcji OSX - brak nagłówków nie jest obsługiwany, więc kod powinien zostać zaktualizowany do: PGID = "$ (ps -o pgid" $ PID "| grep [0-9] | tr -d '')"
Maxim Kholyavkin
166
pkill -TERM -P 27888
Spowoduje to zabicie wszystkich procesów, które mają identyfikator procesu nadrzędnego 27888.
W moim szybkim teście pgrep zgłosił tylko bezpośrednie dzieci, więc może to nie zabić całej hierarchii.
haridsv
10
Zgadzam się z @haridsv: pkill -Pwysyła sygnał tylko do dziecka => wnuk nie odbiera sygnału => Dlatego mam wroten inną odpowiedź, aby to wyjaśnić. Pozdrawiam ;-)
olibre
1
Ze skryptu bash, aby zabić własne dzieci, użyj pkill -TERM -P ${$}.
François Beausoleil,
3
@Olyjob, czy nie jest niebezpieczne spać, a potem zabijać? Tymczasem identyfikatory procesów mogły zostać ponownie wykorzystane przez system operacyjny: możesz zabijać procesy, które nie są już Twoimi dziećmi. Podejrzewam, że wezwanie do pkill musiałoby zostać wykonane ponownie, aby temu zapobiec.
François Beausoleil,
2
@Onlyjob FYI, jestem w stanie odrodzić 32768 procesów, jednowątkowych, z lekkim dostępem do I / O w mniej niż 19 sekund na mojej maszynie:$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
uczestniczoi
102
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 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 $@
Te --argumenty psnie działają na OS X, aby pracować tam zastąpić pskomendę: ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/ggdzie _regexjest zdefiniowana przed forpętli:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
Artur
5
Zatrzymane procesy nie są zabijane za pomocą SIGTERM. Zobacz moją odpowiedź
x-yuri,
1
-1 używa #! / Bin / bash zamiast #! / Usr / bin / env bash (lub jeszcze lepiej POSIX tylko konstruuje i / bin / sh)
Good Person
Czy wystarczy wysłać SIGKILL zamiast SIGTERM, aby zagwarantować, że killtree()działa niezawodnie?
David
3
jeśli psnie obsługuje --ppid, można pgrep -P ${_pid}zamiast tego użyć
Hubert Kario
16
Komenda rkill zpakietu pslist wysyła dany sygnał (lubSIGTERMdomyślnie) do określonego procesu i wszystkich jego potomków:
Uproszczenie za pomocą grep, zamiast sed ...pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
anishsane
2
należy dodać -ldo pstreetak 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ć \ndo miejsca, ponieważ będzie działać dobrze), dzięki!
Aquarius Power
9
Zmodyfikowana wersja odpowiedzi zhiganga:
#!/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
możesz wait $pidtylko na rozpoczętych procesach, nie na wszystkich precesjach, więc nie jest to ogólne rozwiązanie
Hubert Kario
@Hubert Kario W takim przypadku czekanie zakończy się z niezerowym statusem i będzie kontynuowało wykonywanie skryptu. Czy się mylę? Ale waitukryje Terminatedwiadomość, jeśli jest to dziecko.
x-yuri
9
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= $PIDbędzie zawierało spacje wiodące dla PID krótszego niż pięć cyfr, ponieważ psuzasadnia 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 psz rurki tr:
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.
W rzeczywistości jest to świetny pomysł i działa bardzo dobrze. Używam go w przypadkach, w których mogę umieścić procesy w tej samej grupie procesów (lub wszystkie są już w tej samej grupie).
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--
Ups, właśnie zdałem sobie sprawę, że podałem tę samą odpowiedź, co Ty => +1. Ale ponadto wyjaśnić, jak po prostu dostać PGIDod PID. Co myślisz? Pozdrawiam
olibre
5
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
}
Myślę, że masz dodatkowe słowo „dziecko” na końcu drugiej linii.
mato
@mato - To nie jest dodatkowe. Ogranicza zakres $childtej funkcji, aby nie zakłócać innych (nielokalnych) zmiennych o tej samej nazwie i zapewnić, że wartość zmiennej lokalnej zostanie wyczyszczona po zakończeniu funkcji.
Adam Katz
Och, rozumiem teraz, myślałem, że to część zadania. Chyba powinienem ponownie przeczytać (wolniej) przed napisaniem komentarza. :) Dzięki.
mato
Przy okazji, czy nie byłoby lepiej stworzyć listę dzieci, a następnie zacząć zabijać od góry (rodzica)? .. W ten sposób moglibyśmy uniknąć sytuacji, gdy rodzic odtworzy dziecko lub kontynuuje uruchamianie następnego kodu, który może potencjalnie zmienić zamierzone zachowanie.
mato
5
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()
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}
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 ...
zwróć uwagę, że skrypt @zhighang SIGSTOP kontroluje proces nadrzędny i dostarcza sygnał do zatrzymanego procesu, więc nie powinno to (AFAIK) powodować wyścigu między procesem tworzenia potomków a dostarczaniem sygnału. Twoja wersja ma jednak wyścig między uzyskaniem listy dzieci a dostarczeniem sygnału do rodzica.
Hubert Kario,
1
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.
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);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
Rozwijam dalej rozwiązania zhigang, xyuri i 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
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.
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;dofor CHILD in $(jobs -s|sort -r);do
kill -s $SIGNAL $CHILD
sleep $MOMENT
donedone
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.";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;
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.
#/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
lubherodes
.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:
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żyjkill -TERM -- -5112
.źródło
pgrep
może zaoferować łatwiejszy sposób znalezienia identyfikatora grupy procesów. Na przykład, aby zabić grupę procesów my-script.sh, uruchomkill -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łuKILL
(9)Możesz pobrać
PGID
z dowolnego ID procesu (PID
) tego samego drzewa procesówkill -- -$(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.Wyjaśnienie
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, wps -o pgid --no-headers $PID
którympgid
można je zastąpićpgrp
.Ale:
ps
wstawia spacje wiodące, gdyPID
ma mniej niż pięć cyfr i jest wyrównany do prawej, jak zauważył tanager . Możesz użyć:PGID=$(ps opgid= "$PID" | tr -d ' ')
ps
z OSX zawsze wypisuje nagłówek, dlatego Speakus proponuje:PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
grep -o [0-9]*
drukuje tylko kolejne cyfry (nie drukuje spacji ani nagłówków alfabetycznych).Dalsze wiersze poleceń
Ograniczenie
kill
wywoływana jest przez proces należącego do tego samego drzewa,kill
ryzykuje się zabić przed zakończeniem całego drzewa zabijania.Długa historia
Uruchom drzewo procesów w tle za pomocą „&”
Polecenie
pkill -P $PID
nie zabija wnuka:Komenda
kill -- -$PGID
zabija wszystkie procesy, w tym wnuka.Wniosek
Zauważam w tym przykładzie
PID
iPGID
są równe (28957
).Właśnie dlatego początkowo myślałem, że
kill -- -$PID
wystarczy. Ale w przypadku, gdy proces jest spawnowany wMakefile
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).źródło
kill
zawsze powinien wysyłać sygnał do całego drzewa przed otrzymaniem własnego sygnału. Ale w niektórych szczególnych okolicznościach / implementacjachkill
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
.Spowoduje to zabicie wszystkich procesów, które mają identyfikator procesu nadrzędnego 27888.
Lub bardziej solidny:
który planuje zabić 33 sekundy później i uprzejmie prosi procesy o zakończenie.
Zobacz tę odpowiedź na zakończenie wszystkich potomków.
źródło
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 ():
źródło
--
argumentyps
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 przedfor
pętli:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
killtree()
działa niezawodnie?ps
nie obsługuje--ppid
, możnapgrep -P ${_pid}
zamiast tego użyćKomenda rkill zpakietu pslist wysyła dany sygnał (lub
SIGTERM
domyślnie) do określonego procesu i wszystkich jego potomków:źródło
Brad też jest tym, co poleciłbym, z tym wyjątkiem, że możesz
awk
całkowicie zrezygnować, jeśli skorzystasz z--ppid
opcjips
.źródło
jeśli wiesz, że przekazałeś pid procesu nadrzędnego, oto skrypt powłoki, który powinien działać:
źródło
Używam nieco zmodyfikowanej wersji metody opisanej tutaj: https://stackoverflow.com/a/5311362/563175
Wygląda to tak:
gdzie 24901 jest PID rodzica.
Wygląda dość brzydko, ale doskonale spełnia swoją funkcję.
źródło
pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
-l
dopstree
tak długie linie nie obcięta; można go uprościć również czytająckill `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:
źródło
wait $pid
tylko na rozpoczętych procesach, nie na wszystkich precesjach, więc nie jest to ogólne rozwiązaniewait
ukryjeTerminated
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 potokups
z rurkitr
:źródło
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
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.
źródło
Zainspirowany komentarzem ysth
źródło
PGID
odPID
. Co myślisz? PozdrawiamPoniż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
:źródło
$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:
źródło
Na podstawie odpowiedzi zhiganga pozwala to uniknąć samobójstwa:
źródło
Jeśli chcesz zabić proces według nazwy:
lub
źródło
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ę
Zawartość killtrees.sh
źródło
Oto odmiana odpowiedzi @ zhigang, która działa bez AWK, opierając się tylko na natywnych możliwościach parsowania Basha:
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.
źródło
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:
Twoje zdrowie.
źródło
jeśli masz pstree i perl w swoim systemie, możesz spróbować tego:
źródło
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
źródło
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.
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.
źródło
Rozwijam dalej rozwiązania zhigang, xyuri i solidsneck:
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.
źródło
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.
Lub w jednym wierszu:
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
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.
źródło
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.
Jeśli nie znasz pid, możesz zagnieżdżać więcej
źródło
źródło
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!
źródło
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,
i obserwuj procesy o nazwie „test” w innym terminalu za pomocą następującego polecenia.
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,
i obserwuj procesy o nazwie „test” w innym terminalu za pomocą następującego polecenia.
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,
Zabijanie procesu potomnego w skrypcie powłoki
źródło
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
źródło
Wiem, że to stare, ale to lepsze rozwiązanie, które znalazłem:
źródło