Como lidar com um processo zumbi no Linux

De forma bem simplificada (ou em termos leigos), processo zumbi é aquele que ainda pode ser visto na tabela de processos, mesmo que já tenha sido finalizado.
Processos nestas condições também são chamados “defuntos” (ou defunct).

Para entender como os zumbis são criados, é necessário compreender como os processos são retirados do espaço da memória do sistema, após serem finalizados
Apesar da coincidência de temas, não há nada de macabro neste post.
Sob o ponto de vista técnico, a presença de zumbis no seu sistema é uma realidade, provavelmente, constante — e normalmente não é preocupante.

Como os zumbis aparecem

A cada vez que um processo completa seu ciclo e termina sua execução com sucesso, ele desocupa a memória e avisa que “morreu” ao processo que lhe deu origem (processo pai).
Neste momento, o pai deve executar uma chamada de sistema (system call) WAIT, que faz a leitura do estado do filho morto.
Quando o WAIT termina de ser executado, o cadáver é removido da memória — e a vida segue.
O problema aparece quando há defeitos no código do processo pai ou, por alguma razão, este é incapaz de ler o estado do processo filho.
Neste caso, a chamada de sistema pode simplesmente não ocorrer.
Como consequência, o cadáver não é removido, permanece na memória e continua aparecendo na tabela de processos.
Toda esta operação, descrita acima, é muito rápida.
Os zumbis são previstos pelo Linux.
São inócuos e, na maioria das vezes, nem são notados.
Muitos tem PID 1 como pai, que é o próprio init.
O init executa um processo de limpeza periódica que acaba por remover naturalmente os zumbis do sistema.
Por isto, é seguro dizer que não há com que se preocupar.

Como encontrar zumbis no Linux

Use o comando ps para checar o seu sistema:


ps axu | grep Z

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
justinc+  1920  0.0  0.0      0     0 tty2     Z+   nov07   0:00 [single] 
justinc+ 20324  0.0  0.0  12792   932 pts/1    S+   17:57   0:00 grep --color=auto Z

Observe a coluna STAT, que informa o estado de cada processo listado.
A letra Z indica quando um processo está em estado zombie.

How to find zombies

Como matar zumbis

Você não pode matar o que já está morto!

Não é possível terminar processos zumbis. Eles já estão finalizados.
Se a situação te preocupa, é possível informar ao processo pai que “sua criança” está morta — para que rodem a chamada de sistema WAIT.
Use o comando pstree para descobrir qual é o PID do processo pai.
Em seguida, é possivel enviar o sinal SIGCHLD para o PID do pai:

kill -s SIGCHLD ppid

Acima, substitua ppid pelo número do PID do processo pai.

Impacto da presença de processos zumbis no sistema

Como já foi dito, ocupam pouco espaço e sua presença, em geral, é inofensiva e sequer chega a ser percebida.
Mas há casos em que a quantidade de zumbis continua a crescer — às vezes rapidamente. Aí, sim, temos um problema.
Como consequencia da multiplicação dos zumbis, podemos nos deparar com 2 stuações desagradáveis:

  1. Cada zumbi mantem seu PID enquanto cria novos processos, com novos PIDs.
    Depois de um tempo, as possibilidades de criar novos PIDs sao exauridas pelo kernel do sistema.
  2. Mesmo que cada zumbi ocupe apenas uma quantidade muito pequena de memoria, um numero muito grande deles pode fazer a diferença e causar a lentidao ou, mesmo, o travamento do sistema.

Você pode agir individualmente em relação a cada zumbi… ou não.
Pode ser legal mostrar aos seus amigos que você tem uma ou outra “criatura” destas habitando o seu sistema, temporariamente.
É possível que o seu amigo também tenha…
Só uma rápida e crescente multiplicação deles é que irá exigir uma medida mais drástica: reboot, para limpar o sistema.

Como manter a execução de um programa, mesmo após finalizar a sessão no Linux

Ao finalizar uma sessão no Linux (na GUI ou na CLI), o sistema operacional entende que todos os programas e processos iniciados pelo usuário devem ser fechados.
Terminar uma sessão não é a mesma coisa que desligar o computador.
Comumente, as pessoas encerram uma sessão, para iniciar outra, sob um novo perfil de usuário.
Este pode ser o momento ideal para executar programas de manutenção, como um script de backup, por exemplo.
O truque pode ser realizado com o utlitário nohup.

O nohup executa comandos imunes a hangups e envia seus resultados (output) a um terminal non-tty.
Em resumo, ele mantém o comando vivo, em execução, mesmo após o fim da sua sessão.
O nome é derivado da junção dos 2 termos: no hangup.

Veja um exemplo, tirado do manual do comando:


nohup wget site.com/file.zip

Outro caso em que ele pode ser útil, é na atualização do sistema.
Se você estava adiando o update, para não sobrecarregar a sua conexão, pode rodar o procedimento ao sair:


nohup sudo apt update

Webmasters podem se valer deste utilitário, para manter procedimentos em funcionamento, mesmo após se desconectarem do servidor remoto.
Como padrão, um arquivo será gerado no diretório local, em que o nohup foi executado, com o nome de nohup.out — contendo as saídas do comando que ele executou.
Para atualizar o sistema, abra um novo terminal e autentique-se como root. Agora rode o procedimento de atualização:


nohup apt update; apt -y full-upgrade


nohup: ignorando entrada e anexando saída a 'nohup.out'
Lendo listas de pacotes... Pronto
Construindo árvore de dependências
Lendo informação de estado... Pronto Calculando atualização... Pronto ...

Pode fechar o terminal.
Quando quiser verificar o andamento da sua atualização, basta dar uma olhada no arquivo nohup.out:


sudo cat nohup.out

linux terminal nohup apt

Como listar processos no Linux em ordem de uso do processador ou da memória

Listar os processos em execução no sistema é fácil.
Neste post, vou mostrar como ordená-los, de maneira que você fique sabendo quem está consumindo mais memória ou quem está usando mais a capacidade de processamento do seu hardware.
O procedimento é simples e pode revelar os vilões do seu sistema. 😉
O comando ps, sozinho, exibe os processos em execução, tal como o comando top.
O top pode ser usado para obter os mesmos resultados, também. Mas vamos ter que abordar seu uso em outro post.
Veja um exemplo de uso do ps:


ps

  PID TTY          TIME CMD
 2037 pts/1    00:00:00 ps
18101 pts/1    00:00:01 bash

O comando ps pode mostrar mais do que apenas isso, com o acréscimo de alguns parâmetros.
Você pode usar o utilitário combinado ao less, para poder ver uma lista maior, com mais conforto:


ps axu | less

Note que os valores de consumo da CPU e da MEMÓRIA se encontram listados na 3a e 4a colunas, respectivamente.
Com o comando, abaixo, vamos ordenar ascendentemente os valores pela coluna 3 (CPU):


ps axu | sort -nk 3

Achou a lista grande? Use o comando tail!


ps axu | sort -nk 3 | tail

www-data 27070  0.0  0.1 405488  9892 ?        S    00:06   0:00 /usr/sbin/apache2 -k start
www-data 32462  0.0  0.1 405488  9892 ?        S    13:35   0:00 /usr/sbin/apache2 -k start
www-data   621  0.0  0.1 362384  7900 ?        S    set25   0:00 php-fpm: pool www
www-data   622  0.0  0.1 362384  7900 ?        S    set25   0:00 php-fpm: pool www
justinc+  1911  0.1  4.7 2044808 375484 tty2   SNl+ set25   5:33 /usr/lib/tracker/tracker-extract
justinc+  1816  0.2  0.1 2215372 15384 ?       S<l  set25   6:33 /usr/bin/pulseaudio --start --log-target=syslog
Root       304  0.5  0.0      0     0 ?        S    set25  18:37 [irq/28-iwlwifi]
justinc+  1790  1.3  4.9 2370480 388544 tty2   Sl+  set25  41:37 /usr/bin/gnome-shell
justinc+  1697  1.5  2.4 514556 194524 tty2    Sl+  set25  49:27 /usr/lib/xorg/Xorg vt2 -displayfd 3 -auth /run/user/1000/gdm/Xauthority -background none -noreset -keeptty -verbose 3
justinc+ 10865 30.4 29.4 5285500 2315972 tty2  Rl+  set25 972:35 /usr/lib/firefox-esr/firefox-esr

O tail pode ser usado para exibir apenas as últimas 10 linhas da saída de um comando.
Para obter os valores referentes a coluna 4 (memória), use o comando da seguinte forma:


ps axu | sort -nk 4 | tail

root       569  0.0  0.4 362384 32712 ?        Ss   set25   0:11 php-fpm: master process (/etc/php/7.0/fpm/php-fpm.conf)
root       851  0.0  0.4 466684 37800 ?        Ssl  set25   0:24 /usr/lib/packagekit/packagekitd
justinc+  1509  0.0  0.5 619264 42560 ?        Ssl  set25   0:25 /usr/lib/gnome-terminal/gnome-terminal-server
mysql      749  0.0  0.8 686288 66008 ?        Ssl  set25   3:06 /usr/sbin/mysqld
justinc+ 18219  0.0  1.1 946796 92580 ?        Sl   set26   0:04 /usr/bin/gnome-software --gapplication-service
Debian-+   659  0.0  1.3 1789592 108340 tty1   Sl+  set25   1:10 /usr/bin/gnome-shell
justinc+  1697  1.5  2.4 515752 194632 tty2    Sl+  set25  49:29 /usr/lib/xorg/Xorg vt2 -displayfd 3 -auth /run/user/1000/gdm/Xauthority -background none -noreset -keeptty -verbose 3
justinc+  1911  0.1  4.7 2044808 375484 tty2   SNl+ set25   5:33 /usr/lib/tracker/tracker-extract
justinc+  1790  1.3  4.9 2370740 389348 tty2   Sl+  set25  41:40 /usr/bin/gnome-shell
justinc+ 10865 30.4 28.8 5282432 2268520 tty2  Sl+  set25 973:25 /usr/lib/firefox-esr/firefox-esr

Fácil, não é?

A evolução do procfs para o sysfs

O Linux herdou do UNIX a metodologia para permitir que programas se informassem sobre os processos em andamento no sistema diretamente das estruturas armazenadas na memória do kernel —— ou seja, lendo diretamente o /dev/mem e interpretando os dados “nus e crus” contidos ali.
Era assim que o comando ps funcionava inicialmente.
Com o tempo, parte da informação passou a ser disponibilizada através de system calls.
Expor dados do sistema diretamente ao espaço do usuário, via /dev/mem se provou uma metodologia ineficiente e insegura, com o tempo.
Um novo método foi introduzido para facilitar o acesso a aos dados estruturados do sistema para os aplicativos do user-space — que foi a criação do sistema de arquivos /proc.
Com o /proc, as interfaces e as estruturas (diretórios e arquivos) poderiam se manter, mesmo havendo mudanças nas camadas internas do kernel.
Este método é menos frágil que o anterior e aguenta melhor o aumento na escala de trabalho do sistema.
O /proc filesystem foi projetado originalmente para publicar informações de processos e alguns atributos chave de sistema, requisitados por comandos como o ‘ps’, o ‘top’ e o ‘free’, por exemplo.
Sendo mais fácil de usar e obter informações a partir deste meio – tanto pelo lado do kernel quanto do lado do user-space – ele se tornou um verdadeiro depósito de informações de todo o tipo, vindas do sistema.
Além disto, ganhou arquivos com permissão de gravação, a serem usados para ajustar configurações e controlar a operação do kernel ou algum de seus vários subsistemas.
Em resumo, o aumento no uso do /proc para implementar controle de interfaces contribuiu para reduzir sua eficiência.

Isto levou os desenvolvedores a implementar, no kernel 2.6, uma nova metodologia, através do sysfs.
O sysfs ou /sys filesystem já foi projetado para adicionar suporte à estrutura do /proc e prover uma maneira uniforme de expor as informações do sistema e pontos de controle (sistema ajustável e atributos de drivers) para o user-space a partir do kernel.
Agora o framework de drivers, dentro do kernel, cria automaticamente os diretórios sob /sys a cada vez que um driver é registrado, baseado em seu tipo e nos valores contidos nas suas estruturas de dados.
Isto significa que os drivers de um tipo particular terão todos os mesmos elementos exibidos via sysfs.
O Linux vive uma fase de transição. Hoje, muitas informações “legadas” do sistema ainda são acessíveis no /proc. Contudo, todos os novos bus e drivers precisam expor suas informações e pontos de controle via sysfs — é para lá que estamos caminhando.

Referências

http://unix.stackexchange.com/questions/4884/what-is-the-difference-between-procfs-and-sysfs.

Systemd para administradores, parte 2

Na primeira parte desta série de artigos sobre o systemd, procurei mostrar como é simples obter uma relação dos serviços disparados durante a inicialização do sistema.
Depois do boot, o systemd segue seu trabalho iniciando outros processos.
Neste texto vamos abordar alguns métodos para descobrir qual processo é dono de quais outros processos.
systemd logo
Há um número substancial de processos rodando, por padrão, na maioria dos sistemas GNU/Linux.
Saber o que cada processo faz e a que ele pertence, pode ser uma tarefa difícil para um administrador.
Alguns serviços mantém certos processos ativos, que “se misturam”, na saída do comando ps, e podem ser difíceis de identificar.
Este é o caso, por exemplo, de daemons que geram arbitrariamente processos e mais processos.
Uma forma de remediar esta situação é exibir a relação de processos por ordem de herança, através do comando ‘ps xaf’:

ps xaf

Segue o resultado parcial, obtido no meu sistema:


...

 2440 ?        Ssl    0:00 /usr/sbin/gdm3
 2450 ?        Sl     0:00  \_ gdm-session-worker [pam/gdm-launch-environment]
 2468 tty1     Ssl+   0:00  |   \_ /usr/lib/gdm3/gdm-wayland-session gnome-sessi
 2470 tty1     S+     0:00  |       \_ dbus-daemon --print-address 3 --session
 2471 tty1     Sl+    0:00  |       \_ /usr/lib/gnome-session/gnome-session-bina
 2481 tty1     Sl+    0:04  |           \_ /usr/bin/gnome-shell
 2507 tty1     Sl+    0:00  |           |   \_ /usr/bin/Xwayland :1024 -rootless
 2629 tty1     Sl+    0:00  |           \_ /usr/lib/gnome-settings-daemon/gnome-
 2671 ?        Sl     0:00  \_ gdm-session-worker [pam/gdm-password]
 2719 tty2     Ssl+   0:00      \_ /usr/lib/gdm3/gdm-x-session --run-script defa
 2721 tty2     S+    10:15          \_ /usr/lib/xorg/Xorg vt2 -displayfd 3 -auth
 2754 tty2     S+     0:00          \_ dbus-daemon --print-address 4 --session
 2756 tty2     Sl+    0:00          \_ /usr/lib/gnome-session/gnome-session-bina
 2813 ?        Ss     0:00              \_ /usr/bin/ssh-agent x-session-manager
 2843 tty2     Sl+    4:55              \_ /usr/bin/gnome-shell
 2981 tty2     Sl+    0:01              \_ zeitgeist-datahub
 2982 tty2     Sl+    0:00              \_ /usr/bin/python3 /usr/share/system-co
 2983 tty2     Sl+    0:00              \_ /usr/lib/evolution/evolution-alarm-no
 3002 tty2     SNl+   0:00              \_ /usr/lib/tracker/tracker-miner-apps
 3003 tty2     SNl+   0:10              \_ /usr/lib/tracker/tracker-miner-fs
 3004 tty2     SNl+   0:00              \_ /usr/lib/tracker/tracker-miner-user-g
 3005 tty2     Sl+    0:01              \_ /usr/bin/gnome-software --gapplicatio
 5613 tty2     Sl+    0:02              \_ /usr/lib/gnome-settings-daemon/gnome-

...

O resultado deste método, contudo, não é muito confíável, uma vez que os processos — cujos ascendentes morrem — tem seu “parentesco” realocado ao PID 1, fazendo com que toda informação sobre sua árvore se perca.
Se um processo sofrer um “double fork, ele já perde seu relacionamento com o processo que o gerou.
Isto não é um defeito e está dentro da lógica tradicional de lidar com daemons do Unix.
O problema é que não serve para o que se quer no momento.
A possibilidade de um processo mudar seu nome – com o PR_SETNAME ou ajustando o argv – é um recurso que também atrapalha este objetivo.
Com os métodos tradicionais de identificação de processos, o administrador pode acabar no meio de um jogo (sem graça) de “esconde-esconde” com os seus processos.

A abordagem do systemd

Com o systemd, colocamos cada processo novo em um grupo de controle (ou control group) — que leva o nome do seu serviço.
Os control groups ou cgroups na sua forma mais básica são simplesmente grupos de processos que pode ser organizados em uma hierarquia e rotulados individualmente.
Quando processos geram processos filhos, estes se tornam automaticamente membros dos cgroups de seus pais. Sem privilégios, não é possível abandonar um cgroup.
Por isto é que os cgroups podem ser usados como meio eficiente de rotular processos a partir do serviço a que cada um pertence. Além de nos dar a certeza de que os serviços não irão se livrar de seus rótulos, independente da quantidade de vezes em que fizerem forks de si mesmos ou se renomearem.
Neste post, quero introduzir dois comandos que podem ajudar a relacionar serviços e processos do systemd.
O primeiro é o (já conhecido) ps e o segundo é o systemd-cgl.

Relacione processos com o comando ps

O comando ps tem opções para exibir informações dos cgroups junto aos outros detalhes dos processos.
Veja um exemplo (com resultado parcial) do uso do comando:

ps xawf -eo pid,user,cgroup,args
 PID  USER     CGROUP                      COMMAND

...

 2410 root     7:pids:/system.slice,3:devi /usr/sbin/irqbalance --pid=/var/run/irqbalance.pid
 2411 root     7:pids:/system.slice,3:devi /usr/lib/policykit-1/polkitd --no-debug
 2421 root     7:pids:/system.slice,3:devi /usr/sbin/cups-browsed
 2429 colord   7:pids:/system.slice,3:devi /usr/lib/colord/colord
 2440 root     7:pids:/system.slice,3:devi /usr/sbin/gdm3
 2450 root     7:pids:/user.slice/user-116  \_ gdm-session-worker [pam/gdm-launch-environment]
 2468 Debian-+ 7:pids:/user.slice/user-116  |   \_ /usr/lib/gdm3/gdm-wayland-session gnome-session --autostart /usr/share/gdm/greete
 2470 Debian-+ 7:pids:/user.slice/user-116  |       \_ dbus-daemon --print-address 3 --session
 2471 Debian-+ 7:pids:/user.slice/user-116  |       \_ /usr/lib/gnome-session/gnome-session-binary --autostart /usr/share/gdm/greete
 2481 Debian-+ 7:pids:/user.slice/user-116  |           \_ /usr/bin/gnome-shell
 2507 Debian-+ 7:pids:/user.slice/user-116  |           |   \_ /usr/bin/Xwayland :1024 -rootless -noreset -listen 4 -listen 5 -displ
 2629 Debian-+ 7:pids:/user.slice/user-116  |           \_ /usr/lib/gnome-settings-daemon/gnome-settings-daemon
 2671 root     7:pids:/user.slice/user-100  \_ gdm-session-worker [pam/gdm-password]
 2719 justinc+ 7:pids:/user.slice/user-100      \_ /usr/lib/gdm3/gdm-x-session --run-script default
 2721 justinc+ 7:pids:/user.slice/user-100          \_ /usr/lib/xorg/Xorg vt2 -displayfd 3 -auth /run/user/1000/gdm/Xauthority -back
 2754 justinc+ 7:pids:/user.slice/user-100          \_ dbus-daemon --print-address 4 --session
 2756 justinc+ 7:pids:/user.slice/user-100          \_ /usr/lib/gnome-session/gnome-session-binary
 2813 justinc+ 7:pids:/user.slice/user-100              \_ /usr/bin/ssh-agent x-session-manager
 2843 justinc+ 7:pids:/user.slice/user-100              \_ /usr/bin/gnome-shell
 2981 justinc+ 7:pids:/user.slice/user-100              \_ zeitgeist-datahub
 2982 justinc+ 7:pids:/user.slice/user-100              \_ /usr/bin/python3 /usr/share/system-config-printer/applet.py
 2983 justinc+ 7:pids:/user.slice/user-100              \_ /usr/lib/evolution/evolution-alarm-notify
 3002 justinc+ 7:pids:/user.slice/user-100              \_ /usr/lib/tracker/tracker-miner-apps
 3003 justinc+ 7:pids:/user.slice/user-100              \_ /usr/lib/tracker/tracker-miner-fs
 3004 justinc+ 7:pids:/user.slice/user-100              \_ /usr/lib/tracker/tracker-miner-user-guides
 3005 justinc+ 7:pids:/user.slice/user-100              \_ /usr/bin/gnome-software --gapplication-service
 5613 justinc+ 7:pids:/user.slice/user-100              \_ /usr/lib/gnome-settings-daemon/gnome-settings-daemon
 2455 root     7:pids:/system.slice,3:devi /sbin/wpa_supplicant -u -s -O /run/wpa_supplicant
 2462 Debian-+ 7:pids:/user.slice/user-116 /lib/systemd/systemd --user
 2463 Debian-+ 7:pids:/user.slice/user-116  \_ (sd-pam)
 2485 root     7:pids:/system.slice,3:devi /usr/lib/upower/upowerd
 2532 Debian-+ 7:pids:/user.slice/user-116 /usr/lib/at-spi2-core/at-spi-bus-launcher
 2551 Debian-+ 7:pids:/user.slice/user-116  \_ /usr/bin/dbus-daemon --config-file=/usr/share/defaults/at-spi2/accessibility.conf --n

...

Na terceira coluna, da listagem, é exibido o cgroup dado pelo systemd a cada processo.
Se você pretende rodar este comando com frequência, crie um alias para ele:

alias psc='ps xawf -eo pid,user,cgroup,args'

Agora, basta usar o alias psc.

Relacione os processos com o systemd-cgls

Talvez você julgue mais adequado relacionar processos com seus cgroups usando a ferramenta systemd-cgls.
Ela exibe cada cgroup hierarquicamente, dentro de uma árvore.
Veja um exemplo:

systemd-cgls

...

│ ├─polkitd.service
│ │ └─2411 /usr/lib/policykit-1/polkitd --no-debug
│ ├─cups-browsed.service
│ │ └─2421 /usr/sbin/cups-browsed
│ ├─NetworkManager.service
│ │ ├─2332 /usr/sbin/NetworkManager --no-daemon
│ │ └─2583 /sbin/dhclient -d -q -sf /usr/lib/NetworkManager/nm-dhcp-helper -pf /
│ ├─irqbalance.service
│ │ └─2410 /usr/sbin/irqbalance --pid=/var/run/irqbalance.pid
│ ├─rsyslog.service
│ │ └─2337 /usr/sbin/rsyslogd -n
│ ├─gdm.service
│ │ └─2440 /usr/sbin/gdm3
│ ├─bluetooth.service
│ │ └─7068 /usr/lib/bluetooth/bluetoothd
│ └─rtkit-daemon.service
    └─2569 /usr/lib/rtkit/rtkit-daemon

...

Novamente, o resultado exibido no meu exemplo está resumido (cortado) para tentar evitar informações irrelevantes para o contexto deste artigo.
Aqui, são exibidos os processos, por cgroups e, consequentemente, por serviços, uma vez que o systemd etiqueta os cgroups pelos seus serviços.

Referências

Outros posts sobre o systemd.
Systemd para administradores – parte 1.
http://0pointer.de/blog/projects/systemd-for-admins-2.html.