Processar descendentes

20

Estou tentando construir um contêiner de processo. O contêiner acionará outros programas. Por exemplo - um script bash que inicia tarefas em segundo plano em execução com o uso de '&'.

A característica importante que procuro é a seguinte: quando eu matar o contêiner, tudo o que foi gerado por ele deve ser morto. Não apenas filhos diretos, mas também seus descendentes.

Quando iniciei este projeto, acreditei erroneamente que quando você matava um processo, os filhos também eram automaticamente mortos. Procurei conselhos de pessoas que tiveram a mesma ideia incorreta. Embora seja possível captar um sinal e transmitir a morte para as crianças, não é isso que estou procurando aqui.

Eu acredito no que eu quero que seja possível, porque quando você fecha um xterm, qualquer coisa que esteja sendo executada nele é morta, a menos que não tenha sido copiada. Isso inclui processos órfãos. É isso que estou procurando recriar.

Eu tenho uma idéia de que o que eu estou procurando envolve sessões unix.

Se houvesse uma maneira confiável de identificar todos os descendentes de um processo, seria útil poder enviar a eles também sinais arbitrários. por exemplo, SIGUSR1.

Craig Turner
fonte
Bem, matar o processo pai envia SIGHUPpara os processos filhos diretos. O manipulador padrão do sinal de desligamento interrompe a execução do processo, portanto a rota padrão é matar todos os descendentes. Leia mais sobre processos e grupos de processos.
alex
Fechar o xterm mata tudo gerado no xterm porque o TTY é destruído. Se você conseguir criar um tty que os processos filho possam usar, poderá destruir o TTY e realizar a mesma coisa. Qualquer processo que não encerre esse TTY (nohup e amigos) receberá um SIGHUP.
Patrick

Respostas:

23

Se você enviar um sinal para um processo, esse processo será interrompido. Eu me pergunto como começou o boato de que matar um processo também mata outros processos, parece particularmente contra-intuitivo.

No entanto, existem maneiras de matar mais de um processo. Mas você não estará enviando um sinal para um processo. Você pode eliminar todo um grupo de processos enviando um sinal para -1234, em que 1234 é o PGID (ID do grupo de processos), que é o PID do líder do grupo de processos. Quando você executa um pipeline , o pipeline inteiro inicia como um grupo de processos (os aplicativos podem mudar isso chamando setpgidou setpgrp).

Quando você inicia processos em segundo plano ( foo &), eles estão em seu próprio grupo de processos. Grupos de processos são usados ​​para gerenciar o acesso ao terminal; normalmente apenas o grupo de processos em primeiro plano tem acesso ao terminal. Os trabalhos em segundo plano permanecem na mesma sessão , mas não há possibilidade de matar uma sessão inteira ou até de enumerar os grupos ou processos de uma sessão, de modo que isso não ajuda muito.

Quando você fecha um terminal, o kernel envia o sinal SIGHUPpara todos os processos que o possuem como terminal de controle . Esses processos formam uma sessão , mas nem todas as sessões têm um terminal de controle. Para o seu projeto, uma possibilidade é, portanto, iniciar todos os processos em seu próprio terminal, criados por script , tela etc. Mate o processo do emulador de terminal para matar os processos contidos (supondo que eles não tenham se separado setsid).

Você pode fornecer mais isolamento executando os processos como seu próprio usuário, que não faz mais nada. Então é fácil eliminar todos os processos: execute kill(a chamada do sistema ou o utilitário ) como esse usuário e use -1 como argumento PID para eliminar, significando "todos os processos desse usuário".

Você pode fornecer ainda mais isolamento, mas com consideravelmente mais configuração executando os processos contidos em um contêiner real .

Gilles 'SO- parar de ser mau'
fonte
Se você programar seus próprios processos, então você pode usar algo como: prctl(PR_SET_PDEATHSIG, SIGHUP);, mais informações: man7.org/linux/man-pages/man2/prctl.2.html
Alexis Wilke
Gilles, você menciona isso - "Quando você fecha um terminal, o kernel envia o sinal SIGHUP para todos os processos que o possuem como terminal de controle". A partir disso , suponho que o terminal envie SIGHUP ao líder da sessão (shell), que o encaminha a todos os grupos de processos da sessão. Qual dos dois estaria certo?
Iruvar
4

No script pai, prenda o sinal de matança e faça com que mate todos os filhos. Por exemplo,

#!/bin/bash
# kill the parent and children together
trap "kill 0" EXIT
# create all the children
for n in $(seq 1 100)
do
    ( echo "begin $n"; sleep 60; echo "end $n" ) &
done
# wait for the children to complete
wait
Andrew Gilmartin
fonte
2

Uma maneira confiável de identificar todos os descendentes de um processo é usar o comando em pstree <pid>que pid é seu ID de processo pai.

Leia a página de manual pstree aqui .

Para sinalizar todos os membros de um grupo de processos: em killpg(<pgrp>, <sig>);
que pgrp é o número do grupo de processos e sig é o sinal.

Para aguardar filhos em um grupo de processos especificado: waitpid(-<pgrp>, &status, ...);

Uma alternativa ao que você está fazendo é executar o contêiner do processo em um novo shell bash. Crie o novo shell bash com o comando bashe execute seus processos. Quando você quiser que todos os processos a serem encerrados, sair do shell com o comando exit.

Chris Ting
fonte
Eu acho que o killpg me permitirá fazer o que preciso. Obrigado.
Craig Turner
1

Usar

unshare -fp --kill-child -- yourprogram

Se você matar unshare, todos os processos filhos (que yourprogrampodem ter gerado) serão mortos.

Agora isso é possível com o util-linux 2.32; Eu implementei isso a montante . Requer espaços de nome de usuário (opção de configuração do kernel CONFIG_USER_NS=y) ou privilégios de root. Veja também aqui .

nh2
fonte
0

O comando rkill do pacote pslist envia o sinal fornecido (ou SIGTERMpor padrão) para o processo especificado e todos os seus descendentes:

rkill [-SIG] pid/name...
Onlyjob
fonte
0

Outra opção para matar todos os descendentes da concha: jobs -p | xargs -n 1 pkill -P

Doug Fawley
fonte