Suponha que eu tenha um processo que gera exatamente um processo filho. Agora, quando o processo pai termina por qualquer motivo (normalmente ou de forma anormal, por morte, ^ C, afirmar falha ou qualquer outra coisa), quero que o processo filho morra. Como fazer isso corretamente?
Alguma pergunta semelhante sobre o stackoverflow:
- (perguntado anteriormente) Como posso fazer com que um processo filho saia quando os pais saem?
- (perguntado mais tarde) Os processos filhos criados com fork () são automaticamente mortos quando o pai é morto?
Alguma pergunta semelhante sobre o stackoverflow para Windows :
prctl()
maneira livre de condições de corrida. Aliás, a resposta vinculada por Maxim está incorreta.man prctl
diz: Defina o sinal de morte do processo pai do processo de chamada como arg2 (um valor de sinal no intervalo 1..maxsig ou 0 para limpar). Este é o sinal que o processo de chamada receberá quando seu pai morrer. Este valor é limpo para o filho de um fork (2) e (desde o Linux 2.4.36 / 2.6.23) ao executar um binário set-user-ID ou set-group-ID.Estou tentando resolver o mesmo problema e, como meu programa deve ser executado no OS X, a solução somente para Linux não funcionou para mim.
Cheguei à mesma conclusão que as outras pessoas nesta página - não há uma maneira compatível com POSIX de notificar uma criança quando um pai morre. Por isso, julguei a melhor coisa - fazer a pesquisa infantil.
Quando um processo pai morre (por qualquer motivo), o processo pai da criança se torna o processo 1. Se a criança simplesmente pesquisar periodicamente, poderá verificar se seu pai é 1. Se for, o filho deve sair.
Isso não é ótimo, mas funciona e é mais fácil do que as soluções de pesquisa de soquete / arquivo de bloqueio TCP sugeridas em outras partes desta página.
fonte
gettpid()
ele não se torna 1, mas obtém opid
agendador da zona (processozsched
).Eu consegui isso no passado executando o código "original" no "filho" e o código "gerado" no "pai" (ou seja: você inverte o sentido usual do teste depois
fork()
). Em seguida, prenda o SIGCHLD no código "gerado" ...Pode não ser possível no seu caso, mas bonito quando funciona.
fonte
Se você não conseguir modificar o processo filho, tente algo como o seguinte:
Isso executa o filho de dentro de um processo de shell com o controle de trabalho ativado. O processo filho é gerado em segundo plano. O shell aguarda uma nova linha (ou um EOF) e mata o filho.
Quando o pai morre - não importa qual seja o motivo -, ele fecha o final do tubo. O shell filho receberá um EOF da leitura e continuará a matar o processo filho em segundo plano.
fonte
dup2
e assumir o comando stdin usando oread -u
sinalizador para ler um descritor de arquivo específico. Também adicionei umsetpgid(0, 0)
na criança para impedir que ela saia ao pressionar ^ C no terminal.dup2()
chamada está incorreta. Se você deseja usarpipes[0]
como stdin, deve escrever emdup2(pipes[0], 0)
vez dedup2(0, pipes[0])
. Édup2(oldfd, newfd)
onde a chamada fecha um newfd aberto anteriormente.No Linux, você pode instalar um sinal de morte pai na criança, por exemplo:
Observe que armazenar o ID do processo pai antes da bifurcação e testá-lo no filho depois
prctl()
elimina uma condição de corrida entreprctl()
e a saída do processo que chamou o filho.Observe também que o sinal de morte dos pais da criança é apagado nos próprios filhos recém-criados. Não é afetado por um
execve()
.Esse teste pode ser simplificado se tivermos certeza de que o processo do sistema encarregado de adotar todos os órfãos tem PID 1:
init
Porém, confiar no processo do sistema e ter o PID 1 não é portátil. POSIX.1-2008 especifica :Tradicionalmente, o processo do sistema que adota todos os órfãos é o PID 1, ou seja, init - que é o ancestral de todos os processos.
Em sistemas modernos como Linux ou FreeBSD, outro processo pode ter esse papel. Por exemplo, no Linux, um processo pode chamar
prctl(PR_SET_CHILD_SUBREAPER, 1)
para se estabelecer como um processo do sistema que herda todos os órfãos de qualquer um de seus descendentes (veja um exemplo no Fedora 25).fonte
init(8)
processo .... a única coisa que você pode assumir é que, quando um processo pai morre, é que seu ID pai será alterado. Na verdade, isso acontece uma vez na vida de um processo ... e é quando o pai do processo morre. Há apenas uma exceção principal para isso, e é parainit(8)
crianças, mas você está protegido contra isso, comoinit(8)
nãoexit(2)
(kernel panic nesse caso)Por uma questão de integridade. No macOS, você pode usar o kqueue:
fonte
NSTask
ou posix spawn. Veja astartTask
função no meu código aqui: github.com/neoneye/newton-commander-browse/blob/master/Classes/…O processo filho possui um canal para / do processo pai? Nesse caso, você receberá um SIGPIPE se estiver escrevendo ou obterá um EOF ao ler - essas condições podem ser detectadas.
fonte
Inspirado por outra resposta aqui, criei a seguinte solução all-POSIX. A idéia geral é criar um processo intermediário entre o pai e o filho, que tem um propósito: observe quando o pai morre e mate explicitamente o filho.
Esse tipo de solução é útil quando o código no filho não pode ser modificado.
Existem duas pequenas advertências com este método:
Como um aparte, o código real que estou usando está em Python. Aqui está a integridade:
fonte
Não acredito que seja possível garantir isso usando apenas chamadas POSIX padrão. Como na vida real, uma vez que uma criança é gerada, ela tem vida própria.
Ele é possível para o processo pai para pegar mais possíveis eventos de terminação, e tentar matar o processo filho nesse ponto, mas há sempre alguns que não pode ser capturado.
Por exemplo, nenhum processo pode pegar a
SIGKILL
. Quando o kernel lida com esse sinal, ele mata o processo especificado sem notificação para esse processo.Para estender a analogia - a única outra maneira padrão de fazê-lo é cometer suicídio quando descobrir que não tem mais um pai ou mãe.
Existe uma maneira única de fazer isso com o Linux
prctl(2)
- veja outras respostas.fonte
Como outras pessoas apontaram, contar com o pid dos pais para se tornar 1 quando os pais saem não é portátil. Em vez de esperar por um ID do processo pai específico, basta aguardar a alteração do ID:
Adicione um micro-sono conforme desejado, se você não quiser pesquisar a toda velocidade.
Essa opção me parece mais simples do que usar um tubo ou depender de sinais.
fonte
getpid()
é feito no pai antes de chamarfork()
. Se o pai morre antes que o filho não exista. O que pode acontecer é a criança viver fora dos pais por um tempo.Instale um manipulador de armadilhas para capturar o SIGINT, que interrompe o processo filho se ele ainda estiver vivo, embora outros pôsteres estejam corretos e não capturem o SIGKILL.
Abra um arquivo .lock com acesso exclusivo e peça ao filho que ele tente abri-lo - se a abertura for bem-sucedida, o processo filho deve sair
fonte
Esta solução funcionou para mim:
Isso era para um processo do tipo trabalhador cuja existência só fazia sentido quando o pai estava vivo.
fonte
Eu acho que uma maneira rápida e suja é criar um cano entre filho e pai. Quando os pais saem, os filhos recebem um SIGPIPE.
fonte
Alguns pôsteres já mencionaram cachimbos e
kqueue
. De fato, você também pode criar um par de soquetes de domínio Unix conectados pelasocketpair()
chamada. O tipo de soquete deve serSOCK_STREAM
.Suponhamos que você tenha os dois descritores de arquivo de soquete fd1, fd2. Agora,
fork()
para criar o processo filho, que herdará o fds. No pai você fecha fd2 e no filho você fecha fd1. Agora, cada processo podepoll()
abrir o fd restante por conta própria para oPOLLIN
evento. Desde que cada lado nãoclose()
explique explicitamente o seu fd durante a vida normal, você pode ter certeza de que umPOLLHUP
sinalizador deve indicar o término do outro (não importa se está limpo ou não). Após a notificação deste evento, a criança pode decidir o que fazer (por exemplo, morrer).Você pode tentar compilar o código de prova de conceito acima e executá-lo em um terminal como
./a.out &
. Você tem aproximadamente 100 segundos para experimentar a eliminação do PID pai por vários sinais, ou ele simplesmente sai. Nos dois casos, você verá a mensagem "filho: pai desligou".Comparado com o método usando
SIGPIPE
manipulador, esse método não requer tentativawrite()
.Este método também é simétrico , ou seja, os processos podem usar o mesmo canal para monitorar a existência um do outro.
Esta solução chama apenas as funções POSIX. Eu tentei isso no Linux e no FreeBSD. Eu acho que deveria funcionar em outros Unixes, mas ainda não testei.
Veja também:
unix(7)
de páginas de manual Linux,unix(4)
FreeBSD,poll(2)
,socketpair(2)
,socket(7)
no Linux.fonte
Em POSIX , as funções
exit()
,_exit()
e_Exit()
são definidas para:Portanto, se você solicitar que o processo pai seja um processo de controle para seu grupo de processos, o filho deverá receber um sinal SIGHUP quando o pai sair. Não tenho certeza absoluta de que isso acontece quando o pai falha, mas acho que sim. Certamente, para os casos sem travamento, deve funcionar bem.
Note que você pode ter que ler um monte de letras miúdas - incluindo a seção Definições Base (Definições), bem como as informações Serviços do Sistema para
exit()
esetsid()
esetpgrp()
- para obter a imagem completa. (Eu também!)fonte
Se você enviar um sinal para o pid 0, usando por exemplo
esse sinal é enviado para todo o grupo de processos, matando efetivamente a criança.
Você pode testá-lo facilmente com algo como:
Se você pressionar ^ D, verá o texto
"Terminated"
como uma indicação de que o intérprete Python foi realmente morto, em vez de apenas sair por causa do fechamento do stdin.fonte
(echo -e "print(2+2)\n" & kill 0) | sh -c "python -"
felizmente imprime 4 em vez de TerminaçãoCaso seja relevante para qualquer outra pessoa, quando eu gerar instâncias da JVM em processos filho bifurcados a partir do C ++, a única maneira de conseguir que as instâncias da JVM sejam encerradas corretamente após o processo pai é fazer o seguinte. Espero que alguém possa fornecer feedback nos comentários se essa não foi a melhor maneira de fazer isso.
1) Chame
prctl(PR_SET_PDEATHSIG, SIGHUP)
o processo filho bifurcado, conforme sugerido antes de iniciar o aplicativo Java viaexecv
e2) Adicione um gancho de desligamento ao aplicativo Java que pesquisa até que seu PID pai seja igual a 1 e faça um esforço
Runtime.getRuntime().halt(0)
. A pesquisa é feita iniciando um shell separado que executa ops
comando (consulte: Como localizo meu PID em Java ou JRuby no Linux? ).EDIT 130118:
Parece que não era uma solução robusta. Ainda estou lutando um pouco para entender as nuances do que está acontecendo, mas às vezes ainda estava obtendo processos órfãos da JVM ao executar esses aplicativos em sessões de tela / SSH.
Em vez de pesquisar o PPID no aplicativo Java, simplesmente solicitei que o gancho de desligamento realizasse a limpeza seguida de uma parada forçada, como acima. Em seguida, certifiquei-me de invocar
waitpid
o aplicativo pai C ++ no processo filho gerado quando era hora de encerrar tudo. Essa parece ser uma solução mais robusta, pois o processo filho garante a finalização, enquanto o pai usa as referências existentes para garantir que seus filhos sejam finalizados. Compare isso com a solução anterior, que teve o processo pai finalizado sempre que quisesse, e os filhos tentaram descobrir se eles ficaram órfãos antes de terminar.fonte
PID equals 1
espera não é válida. O novo pai pode ser outro PID. Você deve verificar se ele muda do pai original (getpid () antes da bifurcação ()) para o novo pai (getppid () na criança não é igual ao getpid () quando chamado antes da bifurcação ()).Outra maneira de fazer isso que é específico do Linux é fazer com que o pai seja criado em um novo espaço de nome do PID. Ele será o PID 1 nesse espaço de nome e, quando sair, todos os seus filhos serão imediatamente mortos
SIGKILL
.Infelizmente, para criar um novo espaço para nome do PID, você precisa ter
CAP_SYS_ADMIN
. Mas, esse método é muito eficaz e não requer nenhuma alteração real nos pais ou nos filhos além do início do pai.Veja clone (2) , pid_namespaces (7) e cancelamento de compartilhamento (2) .
fonte
Se o pai morre, o PPID dos órfãos muda para 1 - você só precisa verificar o seu próprio PPID. De certa forma, isso é pesquisa, mencionado acima. aqui está uma peça de concha para isso:
fonte
Encontrei 2 soluções, ambas não perfeitas.
1. Mate todas as crianças matando (-pid) quando receber o sinal SIGTERM.
Obviamente, esta solução não pode lidar com "kill -9", mas funciona na maioria dos casos e é muito simples porque não precisa se lembrar de todos os processos filhos.
Da mesma forma, você pode instalar o manipulador 'exit' como acima, se chamar process.exit em algum lugar. Nota: Ctrl + C e falha súbita foram processados automaticamente pelo sistema operacional para eliminar o grupo de processos, portanto não há mais aqui.
2.Use chjj / pty.js para gerar seu processo com o terminal de controle conectado.
Quando você mata o processo atual mesmo matando -9, todos os processos filhos também são automaticamente mortos (pelo sistema operacional?). Eu acho que, como o processo atual fica do outro lado do terminal, se o processo atual morrer, o processo filho receberá o SIGPIPE.
fonte
Consegui fazer uma solução portátil, sem polling, com três processos, abusando do controle e das sessões do terminal. Isso é masturbação mental, mas funciona.
O truque é:
Dessa maneira:
Deficiências:
fonte
Mesmo depois de sete anos, acabei de encontrar esse problema, pois estou executando o aplicativo SpringBoot que precisa iniciar o webpack-dev-server durante o desenvolvimento e precisa eliminá-lo quando o processo de back-end parar.
Eu tento usar,
Runtime.getRuntime().addShutdownHook
mas funcionou no Windows 10, mas não no Windows 7.Eu mudei para usar um segmento dedicado que aguarda o encerramento do processo ou
InterruptedException
que parece funcionar corretamente nas duas versões do Windows.fonte
Historicamente, no UNIX v7, o sistema de processos detectou a órfã dos processos, verificando o ID pai de um processo. Como eu digo, historicamente, o
init(8)
processo do sistema é um processo especial por apenas uma razão: ele não pode morrer. Ele não pode morrer porque o algoritmo do kernel para lidar com a atribuição de um novo ID do processo pai depende desse fato. quando um processo executa suaexit(2)
chamada (por meio de uma chamada do sistema de processo ou por uma tarefa externa, enviando um sinal ou algo parecido), o kernel reatribui a todos os filhos deste processo o ID do processo init como o ID do processo pai. Isso leva ao teste mais fácil e mais portátil de saber se um processo ficou órfão. Basta verificar o resultado dagetppid(2)
chamada do sistema e se é o ID do processo doinit(2)
processo, o processo ficou órfão antes da chamada do sistema.Dois problemas emergem dessa abordagem que podem levar a problemas:
init
processo para qualquer processo do usuário; portanto, como podemos garantir que o processo init sempre seja o pai de todos os processos órfãos? Bem, noexit
código de chamada do sistema, há uma verificação explícita para ver se o processo que está executando a chamada é o processo init (o processo com pid igual a 1) e, nesse caso, o kernel entra em pânico (não deve mais ser capaz de manter hierarquia do processo), portanto, não é permitido que o processo init faça umaexit(2)
chamada.1
, mas isso não é garantido pela abordagem POSIX, que afirma (como exposto em outra resposta) que apenas o ID do processo de um sistema é reservado para esse fim. Quase nenhuma implementação posix faz isso, e você pode supor em sistemas derivados do unix originais que ter1
como resposta agetppid(2)
chamada do sistema é suficiente para assumir que o processo é órfão. Outra maneira de verificar é fazer umgetppid(2)
logo após o garfo e comparar esse valor com o resultado de uma nova chamada. Isso simplesmente não funciona em todos os casos, pois as duas chamadas não são atômicas juntas e o processo pai pode morrer após afork(2)
e antes da primeiragetppid(2)
chamada do sistema. Aparent id only changes once, when its parent does an
saída do processo (2)call, so this should be enough to check if the
getppid (2)result changed between calls to see that parent process has exit. This test is not valid for the actual children of the init process, because they are always children of
init (8) `, mas você pode assumir com segurança esses processos como não tendo pai (exceto quando você substitui em um sistema o processo init)fonte
Passei o pai pid usando o ambiente para o filho e verifiquei periodicamente se / proc / $ ppid existe no filho.
fonte