Existe alguma variante UNIX na qual um processo filho morre com seu pai?

41

Estou estudando o comportamento do kernel Linux há algum tempo e sempre ficou claro para mim que:

Quando um processo morre, todos os seus filhos são devolvidos ao initprocesso (PID 1) até que acabem morrendo.

No entanto, recentemente, alguém com muito mais experiência do que eu no kernel me disse que:

Quando um processo termina, todos os filhos também morrem (a menos que você use o NOHUPcaso em que eles retornam init).

Agora, apesar de não acreditar nisso, ainda escrevi um programa simples para garantir isso. Eu sei que não devo confiar no time ( sleep) para testes, pois tudo depende do agendamento do processo; no entanto, para este caso simples, acho que isso é suficiente.

int main(void){
    printf("Father process spawned (%d).\n", getpid());
    sleep(5);

    if(fork() == 0){
        printf("Child process spawned (%d => %d).\n", getppid(), getpid());
        sleep(15);
        printf("Child process exiting (%d => %d).\n", getppid(), getpid());
        exit(0);
    }

    sleep(5);
    printf(stdout, "Father process exiting (%d).\n", getpid());
    return EXIT_SUCCESS;
}

Aqui está a saída do programa, com o psresultado associado sempre que se printffala:

$ ./test &
Father process spawned (435).

$ ps -ef | grep test
myuser    435    392   tty1    ./test

Child process spawned (435 => 436).

$ ps -ef | grep test
myuser    435    392   tty1    ./test
myuser    436    435   tty1    ./test

Father process exiting (435).

$ ps -ef | grep test
myuser    436    1     tty1    ./test

Child process exiting (436).

Agora, como você pode ver, isso se comporta exatamente como eu esperava. O processo órfão (436) é devolvido a init(1) até que ele morra.

No entanto, existe algum sistema baseado em UNIX no qual esse comportamento não se aplica por padrão? Existe algum sistema no qual a morte de um processo desencadeie imediatamente a morte de todos os seus filhos?

John WH Smith
fonte

Respostas:

68

Quando um processo termina, todos os filhos também morrem (a menos que você use o NOHUP; nesse caso, eles retornam ao init).

Isto está errado. Completamente errado. A pessoa que disse que estava enganada ou confundiu uma situação específica com o caso geral.

Existem duas maneiras pelas quais a morte de um processo pode indiretamente causar a morte de seus filhos. Eles estão relacionados ao que acontece quando um terminal é fechado. Quando um terminal desaparece (historicamente porque a linha serial foi cortada devido a uma interrupção do modem, hoje em dia geralmente porque o usuário fechou a janela do emulador de terminal), um sinal SIGHUP é enviado ao processo de controle em execução naquele terminal - normalmente, o shell inicial é iniciado nesse terminal. Os reservatórios normalmente reagem a isso saindo. Antes de sair, os shells destinados ao uso interativo enviam o HUP para cada tarefa iniciada.

Iniciar um trabalho a partir de um shell com nohupinterrupções na segunda fonte de sinais HUP, porque o trabalho ignorará o sinal e, portanto, não será solicitado que ele morra quando o terminal desaparecer. Outras maneiras de interromper a propagação de sinais HUP do shell para os trabalhos incluem o uso do disownbuiltin do shell, se houver um (o trabalho é removido da lista de trabalhos do shell) e o uso de bifurcação dupla (o shell inicia um filho que inicia um filho. própria e sai imediatamente; a concha não tem conhecimento de seu neto).

Novamente, os trabalhos iniciados no terminal morrem não porque o processo pai (o shell) morre, mas porque o processo pai decide matá-los quando lhes é pedido. E o shell inicial no terminal morre não porque seu processo pai morre, mas porque seu terminal desaparece (o que pode ou não ser coincidentemente porque o terminal é fornecido por um emulador de terminal que é o processo pai do shell).

Gilles 'SO- parar de ser mau'
fonte
1
Existe uma terceira maneira, algo com grupos de controle. Tudo o que sei é que o systemd o usa para impor mortes limpas.
o11c
5
@JanHudec Eu acho que você está confundindo nohupcom disown. disowné um componente embutido em algum shell que remove um trabalho em execução da tabela de trabalhos (usando um argumento semelhante %1), e o principal efeito colateral útil disso é que o shell não envia um SIGHUP ao subprocesso. nohupé um utilitário externo que ignora o SIGHUP (e faz poucas outras coisas) e inicia o comando passado em sua linha de comando.
Gilles 'SO- stop be evil'
@ Gilles: Não, eu não estava, mas olhar para o traço nohupde fato ignora o sinal antes de ser execo comando.
Jan Hudec
Obrigado pela sua resposta! Eu gostei particularmente do pouco histórico que havia em relação às interrupções no modem. Eu estava começando a me preocupar que eu tinha sido mal-entendido o kernel todo esse tempo ...
John WH Smith
3
Também deve ser observado que um programa sai quando recebe SIGHUP porque o manipulador de sinal padrão para SIGHUP é sair. Mas qualquer programa pode implementar seu próprio manipulador SIGHUP, o que pode fazer com que ele não saia quando o shell pai o envia SIGHUP.
Slebetman
12

Quando um processo termina, todos os filhos também morrem (a menos que você use o NOHUP; nesse caso, eles retornam ao init).

Isso está correto se o processo for um líder de sessão. Quando um líder da sessão morre, um SIGHUP é enviado a todos os membros dessa sessão. Na prática, isso significa seus filhos e seus descendentes.

Um processo se torna líder de sessão chamando setsid. Os reservatórios usam isso.

Dennis
fonte
Acabei de executar um teste sobre isso, mas não consegui reproduzir o comportamento que você está descrevendo ... Gerou um processo, iniciei uma nova sessão ( forkmate o processo pai setsid) e forcei outro filho para essa nova sessão. No entanto, quando o processo pai (novo líder da sessão) morreu, a criança não recebeu nenhum SIGHUP e se apegou initaté que finalmente saísse.
John WH Smith
Da página de setpgid(2)manual: Se uma sessão tiver um terminal de controle e o sinalizador CLOCAL para esse terminal não estiver definido e ocorrer uma interrupção do terminal, o líder da sessão receberá um SIGHUP. Se o líder da sessão sair, um sinal SIGHUP também será enviado para cada processo no grupo de processos em primeiro plano do terminal de controle.
Ninjalj #
1
@ ninjalj Então, acho que essa resposta é válida se e somente se o líder da sessão também for o processo de controle de um terminal. Um líder de sessão padrão, independente de qualquer terminal, não mata seus filhos. Está correto?
John WH Smith
-1

Portanto, o que os pôsteres acima estão dizendo é: as crianças não morrem, os pais as matam (ou enviam um sinal no qual elas terminam). Portanto, você pode ter o que pede, se programar o Pai para (1) manter um registro de todos os filhos e (2) enviar um sinal para todos os filhos.

É isso que o Shell faz e deve ser o que o processo pai faz. Pode ser necessário capturar o sinal HUP no pai, para que você ainda tenha controle suficiente para matar os filhos.

marco
fonte
Muitas causas podem fazer o pai morrer. Não há como impedir a sobrevivência dos filhos se o pai for SIGKILLed, por exemplo.
Emil Jeřábek apoia Monica
-1

Estou faltando um pouco nas respostas que estão um pouco relacionadas aos pais que estão morrendo: quando um processo escreve em um canal para o qual não há mais processo de leitura, ele recebe um SIGPIPE. A ação padrão para SIGPIPE é finalização.

Isso pode realmente causar processos para morrer. De fato, é a maneira padrão pela qual o programa yesmorre.

Se eu executar

(yes;echo $? >&2)|head -10

no meu sistema, a resposta é

y
y
y
y
y
y
y
y
y
y
141

e 141 é realmente 128 + SIGPIPE:

   SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                 readers

de man 7 signal.

user86542
fonte
2
Mas o processo de leitura do canal não é necessariamente (ou mesmo geralmente) seu pai. Portanto, esse é realmente um caso muito diferente do que a pergunta está fazendo.
Barmar