Grande pergunta clássica sobre gerenciamento de trabalhos e sinais com bons exemplos! Desenvolvi um roteiro de teste simplificado para me concentrar na mecânica do tratamento de sinal.
Para fazer isso, depois de iniciar os filhos (loop.sh) em segundo plano, chame wait
e, após o recebimento do sinal INT, kill
do grupo de processos cujo PGID seja igual ao seu PID .
-
Para o script em questão,
play.sh
, isso pode ser feito da seguinte forma: -
Na função
stop()
, substituaexit 1
porkill -TERM -$$ # note the dash, negative PID, kills the process group
-
Inicie
loop.sh
como um processo em segundo plano (vários processos em segundo plano podem ser iniciados aqui e gerenciados porplay.sh
)loop.sh &
-
Adicione
wait
no final do script para esperar por todas as crianças.wait
Quando o script inicia um processo, esse filho se torna um membro de um grupo de processos com PGID igual ao PID do processo pai, que é $$
no shell pai.
Por exemplo, o script trap.sh
iniciou três sleep
processos em segundo plano e agora está wait
neles, observe que a coluna ID do grupo de processos (PGID) é a mesma do PID do processo pai:
PID PGID STAT COMMAND
17121 17121 T sh trap.sh
17122 17121 T sleep 600
17123 17121 T sleep 600
17124 17121 T sleep 600
No Unix e no Linux, você pode enviar um sinal para cada processo nesse grupo de processos chamando kill
com o valor negativo de PGID
. Se você der a kill
um número negativo, ele será usado como -PGID. Como o PID do script ( $$
) é o mesmo que o PGID, você pode kill
do seu grupo de processos no shell com
kill -TERM -$$ # note the dash before $$
você precisa fornecer um número de sinal ou nome, caso contrário, algumas implementações de kill dirão "Opção ilegal" ou "especificação de sinal inválida".
O código simples abaixo ilustra tudo isso. Ele define um manipulador de sinal trap
, gera 3 filhos e, em seguida, entra em um ciclo de espera sem fim, aguardando para se matar pelo comando kill
process group no manipulador de sinal.
$ cat trap.sh
#!/bin/sh
signal_handler() {
echo
read -p 'Interrupt: ignore? (y/n) [Y] >' answer
case $answer in
[nN])
kill -TERM -$$ # negative PID, kill process group
;;
esac
}
trap signal_handler INT
for i in 1 2 3
do
sleep 600 &
done
wait # don't exit until process group is killed or all children die
Aqui está uma amostra de execução:
$ ps -o pid,pgid,stat,args
PID PGID STAT COMMAND
8073 8073 Ss /bin/bash
17111 17111 R+ ps -o pid,pgid,stat,args
$
OK, não há processos extras em execução. Inicie o script de teste, interrompa-o ( ^C
), escolha ignorar a interrupção e, em seguida, suspenda-a ( ^Z
):
$ sh trap.sh
^C
Interrupt: ignore? (y/n) [Y] >y
^Z
[1]+ Stopped sh trap.sh
$
Verifique os processos em execução, observe os números do grupo de processos ( PGID
):
$ ps -o pid,pgid,stat,args
PID PGID STAT COMMAND
8073 8073 Ss /bin/bash
17121 17121 T sh trap.sh
17122 17121 T sleep 600
17123 17121 T sleep 600
17124 17121 T sleep 600
17143 17143 R+ ps -o pid,pgid,stat,args
$
Traga nosso script de teste para o primeiro plano ( fg
) e interrompa ( ^C
) novamente, desta vez escolha não para ignorar:
$ fg
sh trap.sh
^C
Interrupt: ignore? (y/n) [Y] >n
Terminated
$
Verifique os processos em execução, sem mais sono:
$ ps -o pid,pgid,stat,args
PID PGID STAT COMMAND
8073 8073 Ss /bin/bash
17159 17159 R+ ps -o pid,pgid,stat,args
$
Nota sobre o seu shell:
Eu tive que modificar seu código para que ele fosse executado no meu sistema. Você tem #!/bin/sh
como a primeira linha em seus scripts, mas os scripts usam extensões (do bash ou zsh) que não estão disponíveis em / bin / sh.