Gerenciamento de processos: ps, top, kill, jobs, bg, fg

1. Introdução ao gerenciamento de processos no Bash

No contexto do shell, um processo é qualquer programa em execução no sistema. Cada comando que você digita no terminal gera um ou mais processos. O Bash oferece ferramentas poderosas para monitorar, controlar e gerenciar esses processos.

Processos podem rodar em foreground (primeiro plano) — bloqueando o terminal até sua conclusão — ou em background (segundo plano), permitindo que você continue usando o shell enquanto eles executam.

Cada processo possui um PID (Process ID) único, um PPID (Parent Process ID) que identifica seu processo pai, e pertence a uma sessão. Essa hierarquia é fundamental para entender como os sinais e o controle de jobs funcionam.

2. ps: Monitorando processos em tempo real

O comando ps (process status) é a ferramenta mais básica e versátil para listar processos.

Sintaxe básica:

# Lista todos os processos do sistema no formato BSD
ps aux

# Lista todos os processos no formato System V
ps -ef

# Filtrando por nome com grep
ps aux | grep "nginx"

Visualizando árvore de processos:

# Mostra hierarquia pai-filho
ps -ejH

# Exemplo de saída parcial:
#   PID  PGID   SID  TTY          TIME CMD
#  1234  1234  1234 pts/0    00:00:00 bash
#  5678  1234  1234 pts/0    00:00:00   sleep 100

Personalizando a saída com -o:

# Mostra apenas PID, nome, uso de CPU e memória
ps -eo pid,comm,%cpu,%mem --sort=-%cpu

# Formato customizado para monitoramento
ps -eo pid,ppid,user,start,time,cmd --sort=start

3. top e htop: Monitoramento interativo de recursos

O top é o monitor interativo clássico do Linux, atualizando a lista de processos em tempo real.

Navegando pelo top:

# Iniciar o top
top

# Comandos interativos dentro do top:
# P - ordenar por uso de CPU
# M - ordenar por uso de memória
# k - matar um processo (solicita PID e sinal)
# r - renice (alterar prioridade)
# u - filtrar por usuário

Exemplo prático:

# Para matar um processo dentro do top:
# 1. Pressione 'k'
# 2. Digite o PID
# 3. Digite o sinal (15 para SIGTERM, 9 para SIGKILL)

Alternativas modernas:

# htop - versão mais amigável com cores e atalhos
# Instalação: sudo apt install htop (Debian/Ubuntu)
htop

# btop - alternativa ainda mais moderna com gráficos
# Instalação via snap ou repositórios
btop

No htop, você pode navegar com setas, matar processos com F9, e ajustar prioridades com F7/F8.

4. kill e sinais: Encerrando e controlando processos

O comando kill envia sinais para processos. Cada sinal tem um comportamento específico.

Sinais comuns:

SIGTERM (15) - Terminação gentil (padrão)
SIGKILL  (9) - Mata imediatamente (não pode ser ignorado)
SIGHUP   (1) - Recarregar configuração
SIGSTOP  (19) - Pausa o processo
SIGCONT  (18) - Continua processo pausado

Uso prático:

# Matar por PID
kill 1234                    # Envia SIGTERM
kill -9 1234                 # Força morte imediata
kill -SIGKILL 1234           # Forma explícita

# Matar por nome
killall firefox              # Mata todos os processos chamados "firefox"
killall -9 nginx

# Matar por padrão (mais flexível)
pkill -f "python script.py"  # Mata processos cujo comando contém o padrão
pkill -u usuario             # Mata todos os processos de um usuário

# Enviar para grupo de processos
kill -- -PGID                # Mata todo o grupo

5. Gerenciamento de jobs no shell: jobs, bg, fg

No shell interativo, jobs são processos que você iniciou a partir do terminal atual. O Bash mantém uma tabela de jobs para gerenciá-los.

Comandos essenciais:

# Listar jobs ativos
jobs -l                      # Mostra PID também

# Iniciar processo em background
sleep 100 &                  # O & coloca em background

# Suspender processo em foreground
# Pressione Ctrl+Z enquanto o processo roda

# Colocar job suspenso em background
bg %1                        # Continua job 1 em background

# Trazer job para foreground
fg %1                        # Traz job 1 para foreground

Exemplo completo:

# 1. Iniciar um processo longo
$ sleep 300
^Z                           # Ctrl+Z suspende
[1]+  Stopped                 sleep 300

# 2. Ver jobs
$ jobs -l
[1]+  5678 Suspended: 18     sleep 300

# 3. Colocar em background
$ bg %1
[1]+ sleep 300 &

# 4. Trazer de volta ao foreground
$ fg %1
sleep 300

6. Controlando processos no terminal: suspensão e resumo

A diferença entre suspender (SIGTSTP) e colocar em background é sutil mas importante:

  • Suspender (Ctrl+Z): pausa o processo, mantendo-o na tabela de jobs
  • Background (bg ou &): permite que o processo continue executando

Exemplos práticos:

# Rodar compilação longa em background
make -j4 &                   # Compila usando 4 núcleos em background

# Cuidado: se o terminal fechar, o processo morre
# Soluções:
disown -h %1                 # Remove job da tabela, mas mantém rodando
nohup long_script.sh &       # Ignora SIGHUP (terminal fechando)

Protegendo processos com nohup:

# Script que continua mesmo após logout
nohup ./meu_script.sh > saida.log 2>&1 &

# Para encontrar depois:
ps aux | grep meu_script

7. Prioridades e nice: ajustando recursos da CPU

O Linux usa um sistema de prioridades (nice values) para alocar CPU. Valores variam de -20 (mais prioritário) a 19 (menos prioritário).

Comandos nice e renice:

# Iniciar processo com prioridade baixa
nice -n 10 ./script_pesado.sh

# Iniciar com prioridade alta (requer root)
sudo nice -n -10 ./urgente.sh

# Alterar prioridade de processo já rodando
renice -n 5 -p 1234         # Reduz prioridade do PID 1234
sudo renice -n -5 -p 5678   # Aumenta prioridade (root)

# Monitorar prioridades
ps -eo pid,comm,nice,pri --sort=nice

Quando usar:

# Evitar travamentos: processos pesados com nice alto
nice -n 15 ffmpeg -i video.mp4 output.avi &

# Priorizar processos do usuário
sudo renice -n -10 -u $USER

8. Boas práticas e automação

Script para matar processos por nome ou idade:

#!/bin/bash
# kill_old_processes.sh - Mata processos antigos por nome

PROCESS_NAME="node"
MAX_AGE_HOURS=24

ps aux | grep "$PROCESS_NAME" | grep -v grep | while read line; do
    PID=$(echo $line | awk '{print $2}')
    ELAPSED=$(echo $line | awk '{print $10}')

    # Converte tempo decorrido para horas (simplificado)
    if [[ $ELAPSED == *":"* ]]; then
        HOURS=$(echo $ELAPSED | cut -d: -f1)
        if [ "$HOURS" -gt "$MAX_AGE_HOURS" ]; then
            echo "Matando PID $PID ($PROCESS_NAME) - rodando há $HOURS horas"
            kill -15 $PID 2>/dev/null || kill -9 $PID
        fi
    fi
done

Combinando ps, kill e cron para limpeza automática:

# Cron job que roda a cada hora
# 0 * * * * /usr/local/bin/kill_old_processes.sh

# Script para matar processos zumbis (estado Z)
ps aux | awk '$8=="Z" {print $2}' | xargs -r kill -9 2>/dev/null

Armadilhas comuns:

  • Zombies: processos que já morreram mas ainda estão na tabela (não podem ser mortos, o pai precisa coletá-los)
  • Orphans: processos cujo pai morreu — são adotados pelo init (PID 1)
  • Processos órfãos: podem continuar rodando sem supervisão

Para evitar problemas, sempre use wait em scripts que disparam processos em background:

#!/bin/bash
job1 &
JOB1_PID=$!
job2 &
JOB2_PID=$!

wait $JOB1_PID
wait $JOB2_PID
echo "Ambos os jobs terminaram"

Referências