Qual é a melhor maneira de enviar um sinal para todos os membros de um grupo de processos?
422
Eu quero matar uma árvore de processo inteira. Qual é a melhor maneira de fazer isso usando qualquer linguagem de script comum? Estou procurando uma solução simples.
Os zumbis devem desaparecer quando o ceifador do sistema for executado. Admito que vi sistemas onde os zumbis permanecem, mas isso é atípico.
Brian Knoblauch
7
Às vezes, esses zumbis remanescentes são responsáveis por alguma atividade assustadora.
precisa
Use um dos comandos chronosou herodes.
Michael Le Barbier Grünewald
8
Você pode
usar o seguinte comando
@ MichaelLeBarbierGrünewald Você poderia, por favor, criar um link para esses programas?
Styrofoam fly
Respostas:
305
Você não diz se a árvore que deseja matar é um único grupo de processos. (Geralmente, esse é o caso se a árvore for o resultado da bifurcação a partir de uma inicialização do servidor ou de uma linha de comando do shell.) Você pode descobrir grupos de processos usando o GNU ps da seguinte maneira:
ps x -o "%p %r %y %x %c "
Se você deseja matar um grupo de processos, basta usar o kill(1)comando, mas em vez de atribuir um número de processo, negue o número do grupo. Por exemplo, para matar todos os processos do grupo 5112, use kill -TERM -- -5112.
kill -74313 -bash: kill: 74313: especificação de sinal inválida Se eu adicionar o kill -15 -GPID, ele funcionou perfeitamente.
Adam Peck
51
Como de costume com quase qualquer comando, se você quiser um argumento normal que começa com um - para não ser interpretado como um switch, precedê-lo com -: matança - -GPID
ysth
9
pgreppode oferecer uma maneira mais fácil de encontrar o ID do grupo de processos. Por exemplo, para matar o grupo de processos do my-script.sh, execute kill -TERM -$(pgrep -o my-script.sh).
Josh Kelley
9
Melhor olhar para stackoverflow.com/questions/392022/… é de longe uma solução mais elegante e, se você precisar listar os pids das crianças, use:ps -o pid --no-headers --ppid $PARENT_PID
Szymon Jeé
4
E se você modificar um pouco o formato e classificar, poderá ver todos os processos bem agrupados e começando com (potencialmente) o pai do grupo em cada grupo:ps x -o "%r %p %y %x %c" | sort -nk1,2
haridsv
199
Mate todos os processos pertencentes à mesma árvore de processos usando o ID do grupo de processos ( PGID)
kill -- -$PGID Use sinal padrão ( TERM= 15)
kill -9 -$PGID Use o sinal KILL(9)
Você pode recuperar a PGIDpartir de qualquer Process-ID ( PID) da mesma árvore de processo
Agradecimentos especiais ao tanager e Speakus pelas contribuições nos $PIDespaços restantes e compatibilidade com OSX.
Explicação
kill -9 -"$PGID"=> Envie o sinal 9 ( KILL) para todos os filhos e netos ...
PGID=$(ps opgid= "$PID")=> Recupere o ID do grupo de processos de qualquer ID de processo da árvore, não apenas o ID dos pais do processo . Uma variação de ps opgid= $PIDé ps -o pgid --no-headers $PIDonde pgidpode ser substituída por pgrp. Mas:
psinsere espaços à esquerda com PIDmenos de cinco dígitos e alinhados à direita, conforme observado pelo tanager . Você pode usar: PGID=$(ps opgid= "$PID" | tr -d ' ')
psdo OSX sempre imprime o cabeçalho, portanto, o Speakus propõe: PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
grep -o [0-9]* imprime apenas dígitos sucessivos (não imprime espaços ou cabeçalhos alfabéticos).
Linhas de comando adicionais
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)
Limitação
Conforme observado por davide e Hubert Kario , quando killé invocado por um processo pertencente à mesma árvore, killcorre o risco de se matar antes de terminar a matança inteira.
Portanto, certifique-se de executar o comando usando um processo com um ID de grupo de processos diferente .
Execute a árvore de processos em 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
O comando pkill -P $PIDnão mata o neto:
> 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
O comando kill -- -$PGIDmata todos os processos, incluindo o neto.
> 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
Conclusão
Percebo neste exemplo PIDe PGIDsão iguais ( 28957).
É por isso que originalmente pensei que kill -- -$PIDera suficiente. Mas, no caso em que o processo é gerado em um IDMakefile do processo, é diferente do ID do grupo .
Eu acho que kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)é o melhor truque simples para matar uma árvore de processo inteira quando chamada de uma ID de grupo diferente (outra árvore de processo).
Olá @davide. Boa pergunta. Eu acho que killdeve sempre enviar o sinal para toda a árvore antes de receber seu próprio sinal. Mas, em algumas circunstâncias / implementações específicas, killpode enviar para si o sinal, ser interrompido e, em seguida, receber seu próprio sinal. No entanto, o risco deve ser mínimo o suficiente e pode ser ignorado na maioria dos casos, porque outros erros devem ocorrer antes deste. Esse risco pode ser ignorado no seu caso? Além disso, outras respostas têm esse bug comum ( killparte da árvore do processo sendo morta). Espero que esta ajuda .. Cheers;)
olibre 13/11
3
Isso só funciona se os subcomandos em si não se tornarem líderes de grupo. Mesmo ferramentas simples como manessas fazem isso. Por outro lado, se você quiser matar o processo gandchild do processo filho, kill -- -$pidnão funcionará. Portanto, não é uma solução genérica.
quer
1
O exemplo é o "filho" tentando matar seus filhos (então netos do comando iniciado pelo usuário). IOW, tente eliminar a hierarquia do processo em segundo plano child.sh.
precisa
1
> Matar -sair - "$ PGID" # mesmo sinal que [CRTL + C] do teclado ---- PARADO deve ser substituído para INT para ser verdade
Maxim Kholyavkin
1
para opção OSX - nenhum cabeçalho não é suportado, portanto, o código deve ser atualizado para: PGID = "$ (ps -o pgid" $ PID "| grep [0-9] | tr-d '')"
Maxim Kholyavkin
166
pkill -TERM -P 27888
Isso matará todos os processos que têm o ID do processo pai 27888.
No meu teste rápido, o pgrep relatou apenas os filhos imediatos, portanto, isso pode não matar toda a hierarquia.
precisa
10
Concordo com @haridsv: pkill -Penvia o sinal apenas para o filho => o neto não recebe o sinal => Portanto, escrevi outra resposta para explicar isso. Cheers ;-)
olibre 28/02
1
Em um script bash, para matar seus próprios filhos, use pkill -TERM -P ${$}.
François Beausoleil
3
@Onlyjob, não é perigoso dormir e depois matar? Os IDs do processo podem ter sido reutilizados pelo sistema operacional nesse meio tempo: você pode estar matando processos que não são mais seus filhos. Eu suspeito que a chamada para pkill teria que ser feita novamente para garantir isso.
François Beausoleil
2
@Onlyjob FYI, eu sou capaz de gerar 32768 processos, de thread único, com acesso leve de E / S em menos de 19 segundos na minha máquina:$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
cychoi
102
Para matar uma árvore de processo recursivamente, 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 $@
Os --argumentos para psnão funcionar no OS X. Para fazê-lo funcionar, substitua o pscomando por: ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/gwhere _regexé definido antes do forloop:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
artur
5
Processos interrompidos não são eliminados com o SIGTERM. Veja minha resposta
x-yuri
1
! -1 usos #! / Bin / bash em vez de # / usr / bin / env bash (ou melhor ainda POSIX apenas construções e / bin / sh)!
Boa Pessoa
Seria suficiente enviar um SIGKILL no lugar do SIGTERM para garantir que killtree()funcione de maneira confiável?
Davide 30/10
3
se psnão suporta --ppid, pode-se usar em pgrep -P ${_pid}vez disso
Hubert Kario
16
O comando rkill dopacote pslist envia o sinal fornecido (ouSIGTERMpor padrão) para o processo especificado e todos os seus descendentes:
Algumas versões do ps emitem um aviso se você usar "-ax" em vez de "ax". Assim: para filho em $ (ps -o pid, ppid ax | \ awk "{if (\ $ 2 == $ pid) {print \ $ 1}}")
Simplificar com grep, em vez de sed ...pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
anishsane
2
você deve adicionar -la pstree, assim que as linhas longas não se truncado; também pode ser mais simples de ler kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`(não é necessário converter \npara o espaço, pois funcionará bem), thx!
Poder de Aquário
9
Versão modificada da resposta 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
você pode wait $pidapenas em processos que você começou, nem todos os precessa, assim que esta não é uma solução genérica
Hubert Kario
@Hubert Kario Nesse caso, a espera sairá com um status diferente de zero e continuará executando o script. Estou errado? Mas waitsuprimirá a Terminatedmensagem se for uma criança.
x-yuri
9
Como não posso comentar (não há reputação suficiente), sou forçado a adicionar uma nova resposta , mesmo que essa não seja realmente uma resposta.
Há um pequeno problema com a resposta muito agradável e completa dada por @olibre em 28 de fevereiro. A saída de ps opgid= $PIDconterá espaços à esquerda para um PID menor que cinco dígitos porque psjustifica a coluna (alinhar os números corretamente). Dentro de toda a linha de comando, isso resulta em um sinal negativo, seguido por espaço (s), seguido pelo grupo PID. Solução simples é tubulação pspara tra espaços Remover:
A função setsid () deve criar uma nova sessão, se o processo de chamada não for um líder de grupo de processos. Após o retorno, o processo de chamada será o líder da sessão desta nova sessão, o líder do grupo de processos de um novo grupo de processos e não terá terminal de controle. O ID do grupo de processos do processo de chamada deve ser definido igual ao ID do processo de chamada. O processo de chamada deve ser o único processo no novo grupo de processos e o único processo na nova sessão.
Entendo que você pode criar um grupo a partir do processo inicial. Eu usei isso no php para poder matar uma árvore de processo inteira depois de iniciá-la.
Isso pode ser uma má ideia. Eu estaria interessado em comentários.
Na verdade, essa é uma ótima idéia e funciona muito bem. Estou usando-o nos casos em que posso colocar processos no mesmo grupo de processos (ou eles já estão no mesmo grupo).
em vez de atribuir um número de processo, negue o número do grupo. Como de costume em quase qualquer comando, se você deseja que um argumento normal que comece com -a não seja interpretado como uma opção, anteceda-o com--
Opa, acabo de perceber que dei a mesma resposta que você => +1. Mas, além disso, eu explico como simplesmente obter PGIDa partir PID. O que você acha? Cheers
olibre
5
A seguinte função shell é semelhante a muitas das outras respostas, mas funciona no Linux e no BSD (OS X, etc) sem dependências 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
}
Eu acho que você tem a palavra extra "filho" no final da 2ª linha.
mato
@ mato - Isso não é extra. Limita o escopo $childdessa função para não perturbar outras variáveis (não locais) com o mesmo nome e garantir que o valor da variável local seja limpo após o término da função.
Adam Katz
Oh, entendo agora, pensei que isso fazia parte da tarefa. Acho que devo reler (mais devagar) antes de escrever um comentário. :) Obrigado.
mato
Aliás, não seria melhor criar uma lista de filhos e começar a matar de cima (pai)? .. Assim, poderíamos evitar uma situação em que um pai recria um filho ou continua executando o próximo código que pode potencialmente alterar o comportamento pretendido.
mato
5
É super fácil fazer isso com python usando psutil . Basta instalar o psutil com o pip e você terá um conjunto completo de ferramentas de manipulação de processos:
def killChildren(pid):
parent = psutil.Process(pid)for child in parent.get_children(True):if child.is_running():
child.terminate()
Parece funcionar bem em Macs e Linux. Em situações nas quais você não pode confiar em poder gerenciar grupos de processos - como ao escrever scripts para testar um software que deve ser construído em vários ambientes - essa técnica de caminhar em árvores é definitivamente útil.
Obrigado pela sua sabedoria, pessoal. Meu script estava deixando alguns processos filhos na saída e a dica de negação tornou as coisas mais fáceis. Eu escrevi essa função para ser usada em outros scripts, se necessário:
# 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}
Provavelmente é melhor matar os pais antes dos filhos; caso contrário, o pai pode gerar novos filhos novamente antes de ser morto. Estes vão sobreviver ao assassinato.
Minha versão do ps é diferente daquela acima; talvez muito velho, portanto o estranho grepping ...
Usar um script de shell em vez de uma função de shell tem muitas vantagens ...
observe que o script @zhighang SIGSTOPs o processo pai e entrega o sinal ao processo parado, portanto, isso não deve (AFAIK) causar uma condição de corrida entre o processo de criação de filhos e a entrega do sinal. Sua versão, porém, tem uma corrida entre obter a lista de filhos e sinalizar a entrega aos pais.
quer
1
O seguinte foi testado no FreeBSD, Linux e MacOS X e depende apenas do pgrep e kill (as versões ps -o não funcionam no BSD). O primeiro argumento é o pai e pai dos quais os filhos devem ser encerrados. O segundo argumento é um booleano para determinar se o pid pai também deve ser finalizado.
Isso enviará o SIGTERM a qualquer processo filho / neto em um script de shell e, se o SIGTERM não for bem-sucedido, ele aguardará 10 segundos e enviará kill.
Resposta anterior:
O seguinte também funciona, mas mata o próprio shell no 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
Desenvolvo a solução de zhigang, xyuri e 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 versão evitará matar sua ancestralidade - o que causa uma enxurrada de processos filhos nas soluções anteriores.
Os processos são interrompidos corretamente antes que a lista de filhos seja determinada, para que nenhum novo filho seja criado ou desapareça.
Após serem mortos, os trabalhos interrompidos precisam continuar para desaparecer do sistema.
Pergunta antiga, eu sei, mas todas as respostas parecem continuar chamando ps, das quais eu não gostei.
Essa solução baseada em awk não requer recursão e chama ps apenas uma 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)
}'
Ou em uma única linha:
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)}'
Basicamente, a idéia é que construamos uma matriz (a) de entradas pai: filho e, em seguida, percorra a matriz encontrando filhos para nossos pais correspondentes, adicionando-os à nossa lista de pais (p) à medida que avançamos.
Se você não deseja interromper o processo de nível superior, faça
sub(/[0-9]*/,"", p)
pouco antes da linha system () a removeria do conjunto de interrupções.
Lembre-se de que existe uma condição de corrida aqui, mas isso é verdade (até onde posso ver) de todas as soluções. Ele faz o que eu precisava, porque o script para o qual eu precisava não cria muitas crianças de vida curta.
Um exercício para o leitor seria transformá-lo em um loop de 2 passagens: após a primeira passagem, envie SIGSTOP para todos os processos na lista p, depois faça um loop para executar ps novamente e após a segunda passagem envie SIGTERM e depois SIGCONT. Se você não se importa com finais agradáveis, o segundo passe pode ser SIGKILL, suponho.
Se você conhece o detalhe da coisa que deseja matar, geralmente pode ir do ID da sessão e de tudo na mesma sessão. Eu checava duas vezes, mas usei isso para scripts que iniciam o rsyncs em loops que eu quero morrer, e não inicio outro (por causa do loop) como faria se eu tivesse acabado de matar o rsync.
Em sh, o comando jobs listará os processos em segundo plano. Em alguns casos, pode ser melhor eliminar primeiro o processo mais recente, por exemplo, o mais antigo criou um soquete compartilhado. Nesses casos, classifique os PIDs na ordem inversa. Às vezes, você deseja esperar um momento para que os trabalhos gravem algo no disco ou coisas assim antes que parem.
E não mate se não precisar!
for SIGNAL in TERM KILL;dofor CHILD in $(jobs -s|sort -r);do
kill -s $SIGNAL $CHILD
sleep $MOMENT
donedone
e observe os processos que estão tendo o nome como 'test' em outro terminal usando o seguinte comando.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
O script acima criará 4 novos processos filhos e seus pais. Cada processo filho será executado por 10 segundos. Porém, quando o tempo limite de 5s atingir, seus respectivos processos pai matarão esses filhos. Portanto, o filho não poderá concluir a execução (10s). Brinque com esses horários (alterne 10 e 5) para ver outro comportamento. Nesse caso, o filho concluirá a execução em 5 segundos antes de atingir o tempo limite de 10 segundos.
2) Deixe o pai atual monitorar e interromper o processo filho quando o tempo limite for atingido. Isso não criará um pai separado para monitorar cada filho. Além disso, você pode gerenciar todos os processos filhos corretamente dentro do mesmo pai.
Crie test.sh da seguinte maneira,
#!/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;
e observe os processos que estão tendo o nome como 'test' em outro terminal usando o seguinte comando.
watch -n1 'ps x -o "%p %r %c" | grep "test" '
O script acima criará 4 novos processos filhos. Estamos armazenando pids de todos os processos filhos e fazendo um loop sobre eles para verificar se eles concluíram sua execução ou ainda estão em execução. O processo filho será executado até o horário CMD_TIME. Mas se o tempo limite de CNT_TIME_OUT atingir, todos os filhos serão mortos pelo processo pai. Você pode mudar o tempo e brincar com o script para ver o comportamento. Uma desvantagem dessa abordagem é que ela está usando a identificação de grupo para eliminar todas as árvores filhas. Mas o processo pai pertence ao mesmo grupo e, portanto, também é morto.
Pode ser necessário atribuir outra identificação de grupo ao processo pai, se você não quiser que o pai seja morto.
#/bin/sh
while true
do
echo "Enter parent process id [type quit for exit]"
read ppid
if [ $ppid -eq "quit" -o $ppid -eq "QUIT" ];then
exit 0
fi
for i in `ps -ef| awk '$3 == '$ppid' { print $2 }'`
do
echo killing $i
kill $i
done
done
chronos
ouherodes
.Respostas:
Você não diz se a árvore que deseja matar é um único grupo de processos. (Geralmente, esse é o caso se a árvore for o resultado da bifurcação a partir de uma inicialização do servidor ou de uma linha de comando do shell.) Você pode descobrir grupos de processos usando o GNU ps da seguinte maneira:
Se você deseja matar um grupo de processos, basta usar o
kill(1)
comando, mas em vez de atribuir um número de processo, negue o número do grupo. Por exemplo, para matar todos os processos do grupo 5112, usekill -TERM -- -5112
.fonte
pgrep
pode oferecer uma maneira mais fácil de encontrar o ID do grupo de processos. Por exemplo, para matar o grupo de processos do my-script.sh, executekill -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
Mate todos os processos pertencentes à mesma árvore de processos usando o ID do grupo de processos (
PGID
)kill -- -$PGID
Use sinal padrão (TERM
= 15)kill -9 -$PGID
Use o sinalKILL
(9)Você pode recuperar a
PGID
partir de qualquer Process-ID (PID
) da mesma árvore de processokill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')
(sinalTERM
)kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')
(sinalKILL
)Agradecimentos especiais ao tanager e Speakus pelas contribuições nos
$PID
espaços restantes e compatibilidade com OSX.Explicação
kill -9 -"$PGID"
=> Envie o sinal 9 (KILL
) para todos os filhos e netos ...PGID=$(ps opgid= "$PID")
=> Recupere o ID do grupo de processos de qualquer ID de processo da árvore, não apenas o ID dos pais do processo . Uma variação deps opgid= $PID
éps -o pgid --no-headers $PID
ondepgid
pode ser substituída porpgrp
.Mas:
ps
insere espaços à esquerda comPID
menos de cinco dígitos e alinhados à direita, conforme observado pelo tanager . Você pode usar:PGID=$(ps opgid= "$PID" | tr -d ' ')
ps
do OSX sempre imprime o cabeçalho, portanto, o Speakus propõe:PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
grep -o [0-9]*
imprime apenas dígitos sucessivos (não imprime espaços ou cabeçalhos alfabéticos).Linhas de comando adicionais
Limitação
kill
é invocado por um processo pertencente à mesma árvore,kill
corre o risco de se matar antes de terminar a matança inteira.Longa história
Execute a árvore de processos em segundo plano usando '&'
O comando
pkill -P $PID
não mata o neto:O comando
kill -- -$PGID
mata todos os processos, incluindo o neto.Conclusão
Percebo neste exemplo
PID
ePGID
são iguais (28957
).É por isso que originalmente pensei que
kill -- -$PID
era suficiente. Mas, no caso em que o processo é gerado em um IDMakefile
do processo, é diferente do ID do grupo .Eu acho que
kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)
é o melhor truque simples para matar uma árvore de processo inteira quando chamada de uma ID de grupo diferente (outra árvore de processo).fonte
kill
deve sempre enviar o sinal para toda a árvore antes de receber seu próprio sinal. Mas, em algumas circunstâncias / implementações específicas,kill
pode enviar para si o sinal, ser interrompido e, em seguida, receber seu próprio sinal. No entanto, o risco deve ser mínimo o suficiente e pode ser ignorado na maioria dos casos, porque outros erros devem ocorrer antes deste. Esse risco pode ser ignorado no seu caso? Além disso, outras respostas têm esse bug comum (kill
parte da árvore do processo sendo morta). Espero que esta ajuda .. Cheers;)man
essas fazem isso. Por outro lado, se você quiser matar o processo gandchild do processo filho,kill -- -$pid
não funcionará. Portanto, não é uma solução genérica.child.sh
.Isso matará todos os processos que têm o ID do processo pai 27888.
Ou mais robusto:
que planejam matar 33 segundos depois e pedem educadamente que os processos sejam encerrados.
Veja esta resposta para encerrar todos os descendentes.
fonte
pkill -P
envia o sinal apenas para o filho => o neto não recebe o sinal => Portanto, escrevi outra resposta para explicar isso. Cheers ;-)pkill -TERM -P ${$}
.$ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
Para matar uma árvore de processo recursivamente, use killtree ():
fonte
--
argumentos paraps
não funcionar no OS X. Para fazê-lo funcionar, substitua ops
comando por:ps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g
where_regex
é definido antes dofor
loop:local _regex="[ ]*([0-9]+)[ ]+${_pid}"
killtree()
funcione de maneira confiável?ps
não suporta--ppid
, pode-se usar empgrep -P ${_pid}
vez dissoO comando rkill dopacote pslist envia o sinal fornecido (ou
SIGTERM
por padrão) para o processo especificado e todos os seus descendentes:fonte
A resposta de Brad é o que eu recomendaria também, exceto que você pode eliminar
awk
completamente se usar a--ppid
opçãops
.fonte
se você souber passar o pid do processo pai, aqui está um script de shell que deve funcionar:
fonte
Eu uso uma versão um pouco modificada de um método descrito aqui: https://stackoverflow.com/a/5311362/563175
Então parece que:
onde 24901 é o PID dos pais.
Parece muito feio, mas funciona perfeitamente.
fonte
pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
-l
apstree
, assim que as linhas longas não se truncado; também pode ser mais simples de lerkill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`
(não é necessário converter\n
para o espaço, pois funcionará bem), thx!Versão modificada da resposta de zhigang:
fonte
wait $pid
apenas em processos que você começou, nem todos os precessa, assim que esta não é uma solução genéricawait
suprimirá aTerminated
mensagem se for uma criança.Como não posso comentar (não há reputação suficiente), sou forçado a adicionar uma nova resposta , mesmo que essa não seja realmente uma resposta.
Há um pequeno problema com a resposta muito agradável e completa dada por @olibre em 28 de fevereiro. A saída de
ps opgid= $PID
conterá espaços à esquerda para um PID menor que cinco dígitos porqueps
justifica a coluna (alinhar os números corretamente). Dentro de toda a linha de comando, isso resulta em um sinal negativo, seguido por espaço (s), seguido pelo grupo PID. Solução simples é tubulaçãops
paratr
a espaços Remover:fonte
Para adicionar à resposta de Norman Ramsey, pode valer a pena olhar para setsid se você deseja criar um grupo de processos.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html
Entendo que você pode criar um grupo a partir do processo inicial. Eu usei isso no php para poder matar uma árvore de processo inteira depois de iniciá-la.
Isso pode ser uma má ideia. Eu estaria interessado em comentários.
fonte
Inspirado pelo comentário de ysth
fonte
PGID
a partirPID
. O que você acha? CheersA seguinte função shell é semelhante a muitas das outras respostas, mas funciona no Linux e no BSD (OS X, etc) sem dependências externas, como
pgrep
:fonte
$child
dessa função para não perturbar outras variáveis (não locais) com o mesmo nome e garantir que o valor da variável local seja limpo após o término da função.É super fácil fazer isso com python usando psutil . Basta instalar o psutil com o pip e você terá um conjunto completo de ferramentas de manipulação de processos:
fonte
Com base na resposta de zhigang, isso evita a auto-matança:
fonte
Se você deseja matar um processo pelo nome:
ou
fonte
Esta é a minha versão de matar todos os processos filhos usando o script bash. Ele não usa recursão e depende do comando pgrep.
Usar
Conteúdo de killtrees.sh
fonte
Aqui está uma variação da resposta do @ zhigang, que ocorre sem o AWK, contando apenas com as possibilidades de análise nativas do Bash:
Parece funcionar bem em Macs e Linux. Em situações nas quais você não pode confiar em poder gerenciar grupos de processos - como ao escrever scripts para testar um software que deve ser construído em vários ambientes - essa técnica de caminhar em árvores é definitivamente útil.
fonte
Obrigado pela sua sabedoria, pessoal. Meu script estava deixando alguns processos filhos na saída e a dica de negação tornou as coisas mais fáceis. Eu escrevi essa função para ser usada em outros scripts, se necessário:
Felicidades.
fonte
Se você possui pstree e perl no seu sistema, pode tentar o seguinte:
fonte
Provavelmente é melhor matar os pais antes dos filhos; caso contrário, o pai pode gerar novos filhos novamente antes de ser morto. Estes vão sobreviver ao assassinato.
Minha versão do ps é diferente daquela acima; talvez muito velho, portanto o estranho grepping ...
Usar um script de shell em vez de uma função de shell tem muitas vantagens ...
No entanto, é basicamente ideia zhigangs
fonte
O seguinte foi testado no FreeBSD, Linux e MacOS X e depende apenas do pgrep e kill (as versões ps -o não funcionam no BSD). O primeiro argumento é o pai e pai dos quais os filhos devem ser encerrados. O segundo argumento é um booleano para determinar se o pid pai também deve ser finalizado.
Isso enviará o SIGTERM a qualquer processo filho / neto em um script de shell e, se o SIGTERM não for bem-sucedido, ele aguardará 10 segundos e enviará kill.
Resposta anterior:
O seguinte também funciona, mas mata o próprio shell no BSD.
fonte
Desenvolvo a solução de zhigang, xyuri e solidsneck:
Esta versão evitará matar sua ancestralidade - o que causa uma enxurrada de processos filhos nas soluções anteriores.
Os processos são interrompidos corretamente antes que a lista de filhos seja determinada, para que nenhum novo filho seja criado ou desapareça.
Após serem mortos, os trabalhos interrompidos precisam continuar para desaparecer do sistema.
fonte
Pergunta antiga, eu sei, mas todas as respostas parecem continuar chamando ps, das quais eu não gostei.
Essa solução baseada em awk não requer recursão e chama ps apenas uma vez.
Ou em uma única linha:
Basicamente, a idéia é que construamos uma matriz (a) de entradas pai: filho e, em seguida, percorra a matriz encontrando filhos para nossos pais correspondentes, adicionando-os à nossa lista de pais (p) à medida que avançamos.
Se você não deseja interromper o processo de nível superior, faça
pouco antes da linha system () a removeria do conjunto de interrupções.
Lembre-se de que existe uma condição de corrida aqui, mas isso é verdade (até onde posso ver) de todas as soluções. Ele faz o que eu precisava, porque o script para o qual eu precisava não cria muitas crianças de vida curta.
Um exercício para o leitor seria transformá-lo em um loop de 2 passagens: após a primeira passagem, envie SIGSTOP para todos os processos na lista p, depois faça um loop para executar ps novamente e após a segunda passagem envie SIGTERM e depois SIGCONT. Se você não se importa com finais agradáveis, o segundo passe pode ser SIGKILL, suponho.
fonte
Se você conhece o detalhe da coisa que deseja matar, geralmente pode ir do ID da sessão e de tudo na mesma sessão. Eu checava duas vezes, mas usei isso para scripts que iniciam o rsyncs em loops que eu quero morrer, e não inicio outro (por causa do loop) como faria se eu tivesse acabado de matar o rsync.
Se você não conhece o pid, ainda pode aninhar mais
fonte
fonte
kill -9
Realmente não .kill -15
não ajuda.Em sh, o comando jobs listará os processos em segundo plano. Em alguns casos, pode ser melhor eliminar primeiro o processo mais recente, por exemplo, o mais antigo criou um soquete compartilhado. Nesses casos, classifique os PIDs na ordem inversa. Às vezes, você deseja esperar um momento para que os trabalhos gravem algo no disco ou coisas assim antes que parem.
E não mate se não precisar!
fonte
Matando o processo filho no shell script:
Muitas vezes precisamos matar processos filhos que são pendurados ou bloqueados por algum motivo. por exemplo. Problema de conexão FTP.
Existem duas abordagens,
1) Criar novo pai separado para cada filho, que monitorará e interromperá o processo filho assim que o tempo limite for atingido.
Crie test.sh da seguinte maneira,
e observe os processos que estão tendo o nome como 'test' em outro terminal usando o seguinte comando.
O script acima criará 4 novos processos filhos e seus pais. Cada processo filho será executado por 10 segundos. Porém, quando o tempo limite de 5s atingir, seus respectivos processos pai matarão esses filhos. Portanto, o filho não poderá concluir a execução (10s). Brinque com esses horários (alterne 10 e 5) para ver outro comportamento. Nesse caso, o filho concluirá a execução em 5 segundos antes de atingir o tempo limite de 10 segundos.
2) Deixe o pai atual monitorar e interromper o processo filho quando o tempo limite for atingido. Isso não criará um pai separado para monitorar cada filho. Além disso, você pode gerenciar todos os processos filhos corretamente dentro do mesmo pai.
Crie test.sh da seguinte maneira,
e observe os processos que estão tendo o nome como 'test' em outro terminal usando o seguinte comando.
O script acima criará 4 novos processos filhos. Estamos armazenando pids de todos os processos filhos e fazendo um loop sobre eles para verificar se eles concluíram sua execução ou ainda estão em execução. O processo filho será executado até o horário CMD_TIME. Mas se o tempo limite de CNT_TIME_OUT atingir, todos os filhos serão mortos pelo processo pai. Você pode mudar o tempo e brincar com o script para ver o comportamento. Uma desvantagem dessa abordagem é que ela está usando a identificação de grupo para eliminar todas as árvores filhas. Mas o processo pai pertence ao mesmo grupo e, portanto, também é morto.
Pode ser necessário atribuir outra identificação de grupo ao processo pai, se você não quiser que o pai seja morto.
Mais detalhes podem ser encontrados aqui,
Matando processo filho no shell script
fonte
Este script também 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
fonte
Eu sei que é velho, mas essa é a melhor solução que eu encontrei:
fonte