No POSIX, os processos são “relacionados” entre si por meio de duas hierarquias básicas:
A hierarquia dos processos pai e filho.
A hierarquia de sessões e grupos de processos.
Os processos do usuário têm muito controle sobre o último, via setpgid
e setsid
, mas eles têm muito pouco controle sobre o primeiro—o ID do processo pai é definido quando um processo é gerado e alterado pelo kernel quando o pai sai (geralmente para PID 1 ), mas caso contrário não muda. Refletindo sobre isso, tenho me perguntado quão importante é o relacionamento entre pais e filhos.
Aqui está um resumo do meu entendimento até agora:
Os relacionamentos pai-filho são claramente importantes do ponto de vista do processo pai , pois várias syscalls, como
wait
esetpgid
, são permitidas apenas em processos filho.A relação sessão-grupo-processo é claramente importante para todos os processos, tanto para o líder da sessão quanto para outros processos na sessão, uma vez que syscalls
kill
operam em grupos de processos inteiros,setpgid
só podem ser usados para ingressar em um grupo na mesma sessão e todos os processos no grupo de processos em primeiro plano de uma sessão são enviadosSIGHUP
se o líder da sessão sair.Além disso, as duas hierarquias estão claramente relacionadas do ponto de vista do pai, uma vez que
setsid
afeta apenas novos filhos esetpgid
só podem ser usadas em crianças, mas parecem essencialmente não relacionadas do ponto de vista da criança (uma vez que o processo de morte do pai não tem impacto algum no grupo ou sessão de um processo).
No entanto, visivelmente ausente está qualquer razão para um processo filho se importar com o que seu pai atual é. Portanto, tenho a seguinte pergunta: o valor atual de getppid()
tem alguma importância do ponto de vista do processo filho , além de talvez identificar se seu processo de desova saiu ou não?
Para colocar a mesma questão de outra forma, imagine que o mesmo programa é gerado duas vezes, do mesmo pai, de duas maneiras diferentes:
O primeiro filho é gerado da maneira usual,
fork()
seguido porexec()
.O segundo filho é gerado indiretamente: o processo pai chama
fork()
, e o filho também chamafork()
, e é o processo neto que chamaexec()
. O filho imediato sai, então o neto fica órfão e seu PPID é reatribuído ao PID 1.
Nesse cenário hipotético, supondo que tudo o mais seja igual, algum programa razoável tem algum motivo para se comportar de maneira diferente? Até agora, minha conclusão parece ser “não”, já que a sessão permanece inalterada, assim como os descritores de arquivos herdados do processo… mas não tenho certeza.
Nota: Eu não considero “adquirir o PID pai para se comunicar com ele” como uma resposta válida para essa pergunta, uma vez que programas órfãos geralmente não podem confiar que seu PPID seja definido como 1 (alguns sistemas definem o PPID de processos órfãos para alguns outro valor), portanto, a única maneira de evitar uma condição de corrida é adquirir o ID do processo pai por meio de uma chamada para getpid()
antes da bifurcação e, em seguida, usar esse valor no filho.
Quando vi essa pergunta, fiquei bastante interessado porque sei que já vi getppid usado antes .. mas não conseguia lembrar onde. Então, eu me voltei para um dos projetos que eu imaginei que provavelmente usou todos os syscalls do Linux e mais alguns: systemd . Uma pesquisa no GitHub mais tarde e encontrei dois usos que retratam alguns casos de uso mais gerais (também existem alguns outros usos, mas são mais específicos para o systemd):
Em sd-notify . Para algum contexto: o systemd precisa saber quando um serviço foi iniciado para poder iniciar qualquer um que dependa dele. Isso normalmente é feito a partir de um programa C por meio da API sd_notify , que é uma maneira de os daemons informarem ao systemd seu status.
Claro, se você estiver usando um script de shell como um serviço... chamar funções C não é exatamente factível. Portanto, o systemd vem com o comando systemd-notify , que é um pequeno wrapper sobre a API sd_notify. Um problema: o systemd também precisa saber o PID que está enviando a mensagem. Para systemd-notify, esse seria seu próprio PID, que seria um ID de processo de curta duração que desaparece imediatamente. Nao é útil.
Você provavelmente já sabe para onde estou indo: getppid é usado pelo systemd-notify para pegar o PID do processo pai, já que geralmente é o processo de serviço real. Resumindo, getppid pode ser usado por um aplicativo CLI de curta duração para enviar uma mensagem em nome do processo pai.
Uma vez que encontrei isso, outra ferramenta unix que pode usar getppid como esta me veio à mente: polkit, que é uma estrutura de autenticação de processo usada para bloquear coisas como enviar mensagens D-Bus ou executar aplicativos privilegiados. (No mínimo, acho que você viu os prompts de senha da GUI que são exibidos pelos agentes de autenticação do polkit.) O polkit inclui um executável nomeado
pkexec
que pode ser usado um pouco como o sudo, exceto que agora o polkit é usado para autorização. Agora, o polkit precisa saber o PID do processo pedindo autorização... sim, você entendeu, o pkexec usa getppid para encontrar isso .(Ao olhar para isso, também descobri que o agente de autenticação TTY do polkit também o usa .)
Este é um pouco menos interessante, mas ainda notável: getppid é usado para emular PR_SET_PDEATHSIG se o pai tiver morrido no momento em que o sinalizador foi definido. (A bandeira é apenas uma maneira de uma criança receber automaticamente um sinal como SIGKILL se o pai morrer.)
Zombies : Este é um caso extremo, mas é eficaz no sentido de que afeta se um programa é interrompido ou não. A única situação que encontrei em que o comportamento de um processo filho é diferente com base em qual processo ele é pai é no momento em que ele sai. Quando um processo sai,
SIGCHLD
é enviado parappid
o filho. Se o processo pai estiver bloqueado ou não estiver manipulandoSIGCHLD
, o filho será deixado em um estado zumbi até que o sinal de saída seja recebido. Se oppid
filho mudar enquanto ele estiver em um estado zumbi, matando o processo pai e sendo re-pai parainit
, eSIGCHLD
o filho for recebido, o filho terminará de terminar e será colhido.Parece que a variedade de cenários em que um processo precisa de qualquer conhecimento de qualquer outro processo em um sistema, como pai, avô, etc. envolve gerenciamento de processos e/ou comunicação entre processos. Para restringir ainda mais o escopo, o intervalo de operações que aceitam a
pid_t
como argumento ou retornam apid_t
, são principalmente tarefas de sinalização e outras tarefas de gerenciamento de processos. Dada uma gama tão limitada de usos, as únicas razões em que posso pensar para usargetppid
, além das informações de diagnóstico, seriam se o filho precisasse sinalizar o pai ou determinar se o pai ainda está em execução. Por exemplo,mod_md
no Apachehttpd
envia um sinal de reinicialização normal para o processo pai para acionar uma reconfiguração.Embora a "Nota" em sua pergunta descreva uma boa alternativa para
getppid
, ela também parece sugerir um possível uso paragetppid
. Para contextualizar, eu estava assistindo a uma apresentação da PWL Conf 2019 ontem à noite, On the Expressive Power of Programming Languages , e a definição de "expressividade" desse artigo está influenciando minha interpretação da questão. A definição se resume a como o comportamento de um programa pode ser diferente com ou sem a presença de um recurso.Começando com as hipóteses:
O que significa se o pré-
fork
PID não correspondergetppid
? Comparar o préfork
-PID comgetppid
pode realmente fornecer um mecanismo multiplataforma e livre de corrida para um processo filho determinar se ele ficou órfão. Sepre-fork-pid != getppid
então a criança é órfã, caso contrário não é.Isso parece uma coisa incomum de se precisar, ou algo que provavelmente seria escrito usando a compilação específica da plataforma
#ifdef
, quando necessário. Além disso, um efeito semelhante pode ser obtido usandokill 0
o pré-fork
-pid
, no entanto, isso pode gerar falsos positivos em processos de execução suficientemente longos quando o sistema reutiliza IDs de processo "antigos".Portanto, a combinação de um ID de processo pré
fork
-pai imutável e uma chamada "ao vivo"getppid
parece ser um mecanismo altamente confiável e multiplataforma para verificar ou sinalizar um processo pai.