9 Observações sobre seleção de processos nos itens proc.mem e proc.num

Processos modificando sua linha de comando

Alguns programas usam a modificação de sua linha de comando como um método para exibir sua atividade atual. Um usuário pode ver a atividade executando ps e comandos top. Exemplos de tais programas incluem PostgreSQL, Sendmail, Zabbix.

Vamos ver um exemplo do Linux. Vamos supor que queremos monitorar um número de processos do agente Zabbix.

O comando ps mostra os processos de interesse como

$ ps -fu zabbix
       UID PID PPID C STIME TTY TIME CMD
       ...
       zabbix 6318 1 0 12:01 ? 00:00:00 sbin/zabbix_agentd -c /home/zabbix/ZBXNEXT-1078/zabbix_agentd.conf
       zabbix 6319 6318 0 12:01 ? 00:00:01 sbin/zabbix_agentd: coletor [inativo 1 segundo]
       zabbix 6320 6318 0 12:01 ? 00:00:00 sbin/zabbix_agentd: listener #1 [aguardando conexão]
       zabbix 6321 6318 0 12:01 ? 00:00:00 sbin/zabbix_agentd: listener #2 [aguardando conexão]
       zabbix 6322 6318 0 12:01 ? 00:00:00 sbin/zabbix_agentd: listener #3 [aguardando conexão]
       zabbix 6323 6318 0 12:01 ? 00:00:00 sbin/zabbix_agentd: verificações ativas #1 [ocioso 1 segundo]
       ...

Selecionar processos por nome e usuário faz o trabalho:

$ zabbix_get -s localhost -k 'proc.num[zabbix_agentd,zabbix]'
       6

Agora vamos renomear o executável zabbix_agentd para zabbix_agentd_30 e reinicie-o.

ps agora mostra

$ ps -fu zabbix
       UID PID PPID C STIME TTY TIME CMD
       ...
       zabbix 6715 1 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30 -c /home/zabbix/ZBXNEXT-1078/zabbix_agentd.conf
       zabbix 6716 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: coletor [inativo 1 segundo]
       zabbix 6717 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: ouvinte #1 [aguardando conexão]
       zabbix 6718 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: ouvinte #2 [aguardando conexão]
       zabbix 6719 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: ouvinte #3 [aguardando conexão]
       zabbix 6720 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: verificações ativas #1 [ocioso 1 segundo]
       ...

Agora, selecionar processos por nome e usuário produz um resultado incorreto:

$ zabbix_get -s localhost -k 'proc.num[zabbix_agentd_30,zabbix]'
       1

Por que uma simples renomeação de executável para um nome mais longo leva a resultado diferente?

O agente Zabbix começa verificando o nome do processo. /proc/<pid>/status arquivo é aberto e a linha Name é marcada. No nosso caso, o Nome linhas são:

$ grep Nome /proc/{6715,6716,6717,6718,6719,6720}/status
       /proc/6715/status:Nome: zabbix_agentd_3
       /proc/6716/status:Nome: zabbix_agentd_3
       /proc/6717/status:Nome: zabbix_agentd_3
       /proc/6718/status:Nome: zabbix_agentd_3
       /proc/6719/status:Nome: zabbix_agentd_3
       /proc/6720/status:Nome: zabbix_agentd_3

O nome do processo no arquivo status é truncado para 15 caracteres.

Um resultado semelhante pode ser visto com o comando ps:

$ ps -u zabbix
         PID TTY TIME CMD
       ...
        6715? 00:00:00 zabbix_agentd_3
        6716? 00:00:01 zabbix_agentd_3
        6717? 00:00:00 zabbix_agentd_3
        6718? 00:00:00 zabbix_agentd_3
        6719? 00:00:00 zabbix_agentd_3
        6720? 00:00:00 zabbix_agentd_3
        ...

Obviamente, isso não é igual ao valor do nosso parâmetro proc.num[] name zabbix_agentd_30. Não ter correspondido ao nome do processo de arquivo status o agente Zabbix transforma em arquivo /proc/<pid>/cmdline.

Como o agente vê o arquivo "cmdline" pode ser ilustrado executando um comando

$ para i em 6715 6716 6717 6718 6719 6720; do gato /proc/$i/cmdline | awk '{gsub(/\x0/,"<NUL>"); imprimir};'; feito
       sbin/zabbix_agentd_30<NUL>-c<NUL>/home/zabbix/ZBXNEXT-1078/zabbix_agentd.conf<NUL>
       sbin/zabbix_agentd_30: coletor [inativo 1 seg]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>. ..
       sbin/zabbix_agentd_30: ouvinte #1 [aguardando conexão]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
       sbin/zabbix_agentd_30: ouvinte #2 [aguardando conexão]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
       sbin/zabbix_agentd_30: ouvinte #3 [aguardando conexão]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
       sbin/zabbix_agentd_30: verificações ativas #1 [ocioso 1 seg]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>. ..

Os arquivos /proc/<pid>/cmdline em nosso caso contêm arquivos invisíveis, não imprimíveis bytes nulos, usados para encerrar strings na linguagem C. Os bytes nulos são mostrados como "<NUL>" neste exemplo.

O agente Zabbix verifica "cmdline" para o processo principal e leva um zabbix_agentd_30, que corresponde ao nosso valor de parâmetro name zabbix_agentd_30. Assim, o processo principal é contado por item proc.num[zabbix_agentd_30,zabbix].

Ao verificar o próximo processo, o agente leva zabbix_agentd_30: collector [idle 1 sec] do arquivo cmdline e ele não atende ao nosso parâmetro name zabbix_agentd_30. Então, apenas o processo principal que não modifica sua linha de comando, é contado. De outros os processos do agente modificam sua linha de comando e são ignorados.

Este exemplo mostra que o parâmetro name não pode ser usado em proc.mem[] e proc.num[] para selecionar processos neste caso.

Usar o parâmetro cmdline com uma expressão regular adequada produz um resultado correto:

$ zabbix_get -s localhost -k 'proc.num[,zabbix,,zabbix_agentd_30[:]]'
       6

Tenha cuidado ao usar os itens proc.mem[] e proc.num[] para monitoramento programas que modificam suas linhas de comando.

Antes de colocar os parâmetros name e cmdline em proc.mem[] e itens proc.num[], você pode querer testar os parâmetros usando item proc.num[] e comando ps.

Threads do kernel Linux

Threads não podem ser selecionadas com o parâmetro cmdline nos itens proc.mem[] e proc.num[]

Vamos ver um exemplo das threads do kernel:

$ ps -ef| grep kthreadd
       root         2     0  0 09:33 ?        00:00:00 [kthreadd]

Ela pode ser selecionada através do parâmetro name:

$ zabbix_get -s localhost -k 'proc.num[kthreadd,root]'
       1

Mas selecionado pelo parâmetro cmdline não irá funcionar:

$ zabbix_get -s localhost -k 'proc.num[,root,,kthreadd]'
       0

A razão disso é que o agente utiliza a expressão regular definida no parametro cmdline e aplica seu conteúdo ao conteúdo do processo /proc/<pid>/cmdline. Mas para as threads do kernel os arquivos /proc/<pid>/cmdline estarão vazios. Logo, o parâmetro cmdline nunca irá funcionar para elas.

Contagem de threads em itens proc.mem[] e proc.num[]

threads do kernel Linux são contados pelo item proc.num[], mas não relatam memória no item proc.mem[]. Por exemplo:

$ ps -ef | grep kthreadd
       raiz 2 0 0 09:51 ? 00:00:00 [kthreadd]
       
       $ zabbix_get -s localhost -k 'proc.num[kthreadd]'
       1
       
       $ zabbix_get -s localhost -k 'proc.mem[kthreadd]'
       ZBX_NOTSUPPORTED: Não é possível obter a quantidade de memória "VmSize".

Mas o que acontece se houver um processo de usuário com o mesmo nome de um thread do núcleo? Então poderia ficar assim:

$ ps -ef | grep kthreadd
       raiz 2 0 0 09:51 ? 00:00:00 [kthreadd]
       zabbix 9611 6133 0 17:58 pts/1 00:00:00 ./kthreadd
       
       $ zabbix_get -s localhost -k 'proc.num[kthreadd]'
       2
       
       $ zabbix_get -s localhost -k 'proc.mem[kthreadd]'
       4157440

proc.num[] contou tanto a thread do kernel quanto o processo do usuário. proc.mem[] reporta a memória apenas para o processo do usuário e conta o memória de thread do kernel como se fosse 0. Isso é diferente do caso acima quando ZBX_NOTSUPPORTED foi relatado.

Tenha cuidado ao usar os itens proc.mem[] e proc.num[] se o programa name acontece para corresponder a um dos threads.

Antes de colocar parâmetros nos itens proc.mem[] e proc.num[], você pode querer testar os parâmetros usando o item proc.num[] e ps comando.