Atualizar
Este é um daqueles em que eu deveria claramente ter lido a questão com mais cuidado (embora aparentemente este seja o caso com a maioria das respostas a esta pergunta). Deixei a resposta original intacta, porque ela fornece boas informações, mesmo que claramente perca o ponto de vista da questão.
Usando o SID
Acho que a abordagem mais geral e robusta aqui (pelo menos para o Linux) é usar SID (ID de sessão) em vez de PPID ou PGID. É muito menos provável que isso seja alterado por processos filho e, no caso de script de shell, o comando setsid
pode ser usado para iniciar uma nova sessão. Fora do shell, a chamada do sistema setuid
pode ser usada.
Para um shell que é um líder de sessão, você pode matar todos os outros processos da sessão (o shell não se mata):
kill $(ps -s $$ -o pid=)
Observação: o sinal de trailing equals no argumento pid=
remove o cabeçalho da coluna PID
.
Caso contrário, usando chamadas do sistema, a chamada getsid
para cada processo parece ser a única maneira.
Usando um PID namspace
Essa é a abordagem mais robusta, mas as desvantagens são que ela é apenas Linux e que precisa de privilégios de root. Além disso, as ferramentas shell (se usadas) são muito novas e não estão amplamente disponíveis.
Para uma discussão mais detalhada dos namespaces do PID, consulte esta questão - Maneira confiável de processar processos filhos usando 'nsenter:' . A abordagem básica aqui é que você pode criar um novo namespace PID usando o sinalizador CLONE_NEWPID
com a chamada do sistema clone
(ou por meio do comando unshare
).
Quando um processo em um namespace PID é órfão (ou seja, quando o processo pai é concluído), ele é enviado novamente para o processo de namespace PID de nível superior, em vez do init
. Isso significa que você sempre pode identificar todos os descendentes do processo de nível superior percorrendo a árvore de processos. No caso de um shell script, a abordagem PPID abaixo mataria de forma confiável todos os descendentes.
Outras leituras nos namespaces do PID:
- Namespaces em operação, parte 3: Espaços de nomes de PID
- Namespaces em operação, parte 4: mais sobre namespaces de PID
Resposta Original
Matando processos filhos
A maneira mais fácil de fazer isso em um script de shell, desde que pkill
esteja disponível, é:
pkill -P $$
Isso mata todos os filhos do processo atual ( $$
se expande para o PID do shell atual).
Se pkill
não estiver disponível, uma maneira compatível com POSIX é:
kill $(ps -o pid= --ppid $$)
Matando todos os processos descendentes
Outra situação é que você pode querer matar todos os descendentes do processo shell atual, bem como apenas os filhos diretos. Neste caso, você pode usar a função shell recursiva abaixo para listar todos os PIDs descendentes, antes de passá-los como argumentos a serem mortos:
list_descendants ()
{
local children=$(ps -o pid= --ppid "$1")
for pid in $children
do
list_descendants "$pid"
done
echo "$children"
}
kill $(list_descendants $$)
Garfos duplos
Uma coisa a se tomar cuidado, que pode impedir que o acima funcione como esperado, é a técnica do fork()
duplo. Isso é comumente usado durante a daemonização de um processo. Como o nome sugere, o processo a ser iniciado é executado na segunda bifurcação do processo original. Depois que o processo é iniciado, o primeiro fork sai, o que significa que o processo se torna órfão.
Nesse caso, ele se tornará filho do processo init
em vez do processo original do qual foi iniciado. Não existe uma maneira robusta de identificar qual processo era o pai original, portanto, se este for o caso, você não pode esperar poder eliminá-lo sem ter outros meios de identificação (um arquivo PID, por exemplo). No entanto, se esta técnica tiver sido usada, você não deve tentar matar o processo sem uma boa razão.
Leitura adicional: