Quem trabalha no terminal do Linux já passou por isso: você roda um ps aux | grep, localiza um serviço travado consumindo recursos ou prendendo portas, tenta matar pelo nome intuitivo e nada acontece. Ou pior: encerra o processo pai, mas os filhos continuam rodando em segundo plano como processos órfãos.
Recentemente, precisei lidar com essa situação ao gerenciar um serviço em background (antigravity-server) que executava uma hierarquia de processos encadeados em sh e node.
Abaixo, compartilho por que isso acontece no nível de kernel, a diferença entre os atributos de processos e o fluxo correto para encerrar tudo de forma cirúrgica.
1. O problema: identificando a árvore de processos e o estado sl#
Ao inspecionar a máquina para localizar os processos em execução:
ps aux | grep anti
A saída revelou três camadas encadeadas via fork/exec:
root 619129 0.0 0.0 2584 840 ? S 15:22 0:00 sh /root/.antigravity-server/bin/1.21.9-hash/bin/antigravity-server --start-server --host=127.0.0.1 --port=0 --connection-token-file /root/.antigravity-server/.hash.token
root 619139 0.3 1.3 11876396 162812 ? Sl 15:22 0:16 /root/.antigravity-server/bin/1.21.9-hash/node /root/.antigravity-server/bin/1.21.9-hash/out/server-main.js --start-server --host=127.0.0.1 --port=0 --connection-token-file /root/.antigravity-server/.hash.token
root 619356 0.2 0.6 1621680 76072 ? Sl 15:22 0:14 /root/.antigravity-server/bin/1.21.9-hash/node /root/.antigravity-server/bin/1.21.9-hash/out/bootstrap-fork --type=ptyHost --logsPath /root/.antigravity-server/data/logs/20260405T152231
root 666120 0.0 0.0 6416 1936 pts/0 S+ 16:48 0:00 grep anti
Na estrutura:
- PID 619129 (
sh): O script wrapper inicial. - PID 619139 (
node ... server-main.js): O processo mestre da aplicação. - PID 619356 (
node ... --type=ptyHost): O worker forked para gerenciar terminais (PTY).
O significado do estado sl#
Na coluna STAT, a sigla Sl detalha a natureza da execução:
S(Interruptible Sleep): O processo está aguardando eventos externos (I/O de rede, timers ou sinais do sistema operacional).l(Multi-threaded): O binário aloca múltiplas threads nativas em pool para processar eventos e chamadas assíncronas.
2. A armadilha do pkill convencional e a entrega de sinais no kernel#
Se executarmos apenas pkill antigravity, o comando falha silenciosamente.
Como a entrega de sinais funciona por baixo do capô#
- Varredura em
/proc: Opkillbusca entradas em/proc/[pid]/commou/proc/[pid]/cmdline. - A Syscall
kill(2): O utilitário dispara a syscall enviando o sinal padrão SIGTERM (15). - Fila de sinais e
task_struct: O kernel verifica permissões e enfileira o sinal no conjunto de sinais pendentes da estruturatask_structdo processo. - Execução no User-Space: Quando a thread for escalonada ou retornar de uma chamada de sistema, ela processa o sinal.
No entanto, o /proc/[pid]/comm guarda apenas os primeiros 15 caracteres do nome do executável (sh ou node). A string antigravity-server existe apenas nos argumentos. Por isso, a flag -f (full command line) é obrigatória para casar toda a linha de comando.
3. Estratégias de encerramento seguro#
Estratégia 1: Varredura com pgrep e encerramento com pkill#
- Validação prévia com pgrep:
pgrep -a -f "antigravity-server"
- Encerramento gracioso (SIGTERM):
pkill -15 -f "antigravity-server"
- Encerramento forçado (SIGKILL como fallback):
pkill -9 -f "antigravity-server"
Estratégia 2: Encerramento por grupo de processos (PGID)#
Para eliminar pai e filhos de uma só vez sem deixar órfãos, você pode enviar o sinal para todo o Process Group ID (PGID) usando o prefixo negativo:
kill -TERM -619129
Estratégia 3: Gestão nativa via systemd e Cgroups#
Em servidores em produção, a melhor prática é rodar o binário como uma Unit do Systemd:
[Service]
KillMode=control-group
Com KillMode=control-group, quando você executa systemctl stop servico, o Systemd envia sinais a todos os processos registrados naquele Cgroup específico, impedindo que workers gerados via fork() continuem soltos no sistema operacional.
4. O que acontece no kernel e casos especiais#
- Processos órfãos (Init Adoption): Se matarmos apenas o script wrapper (
sh) sem que ele repasse o sinal aos filhos, os processosnodecontinuarão rodando e serão adotados pelo PID 1 (systemd/init). O uso depkill -fouKillMode=control-groupevita isso. - Processos zumbis (
<defunct>): Se um filho encerrar mas o processo pai não executar a chamadawaitpid(), ele permanece na tabela de processos como zumbi. Nesses casos, encerra-se o processo pai para que o PID 1 adote o zumbi e libere a tabela de processos. - Arquivos de lock e tokens: Se o processo utilizar arquivos temporários de autenticação, um
kill -9imediato pode deixar travas no disco. Verifique e remova arquivos órfãos antes de reiniciar.
5. Validação pós-execução e descritores abertos#
Após o encerramento, verifique se sockets e descritores de arquivos foram totalmente liberados:
# Checar sockets e portas ativas
ss -tapen | grep -E '619139|619129'
# Listar descritores de arquivos abertos (caso ainda ativo)
ls -l /proc/619139/fd
# Se o processo estiver em execucao normal (S ou R), inspecione sinais com strace (se estiver em D-state/uninterruptible sleep, aguarde o kernel concluir o I/O)
strace -p 619139 -e trace=signal,exit_group,write
Conclusão: resumo prático e fluxo recomendado#
Para encerrar árvores de processos no Linux de forma definitiva:
- Valide o escopo com
pgrep -a -f "padrao". - Envie
pkill -15 -f "padrao"oukill -TERM -<PGID>para encerramento gracioso. - Utilize
pkill -9 -f "padrao"caso o processo não responda. - Para serviços permanentes, utilize units do Systemd com
KillMode=control-group.
Este artigo foi útil?
Deixe uma reação rápida para apoiar o conteúdo:
Este post está licenciado sob CC BY-NC.



Comentários
Participe da discussão abaixo.
0 comentários