8 Note sulla selezione dei processi negli item proc.mem e proc.num

Processi che modificano la propria command line

Alcuni programmi usano la modifica della propria command line come metodo per visualizzare la loro attività corrente. Un utente può vedere l'attività eseguendo i comandi ps e top. Esempi di tali programmi includono PostgreSQL, Sendmail, Zabbix.

Vediamo un esempio da Linux. Supponiamo di voler monitorare un numero di processi di Zabbix agent.

Il comando ps mostra i processi di interesse come

$ 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/zabbix_agentd.conf               
zabbix    6319  6318  0 12:01 ?        00:00:01 sbin/zabbix_agentd: collector [idle 1 sec]                          
zabbix    6320  6318  0 12:01 ?        00:00:00 sbin/zabbix_agentd: listener #1 [waiting for connection]            
zabbix    6321  6318  0 12:01 ?        00:00:00 sbin/zabbix_agentd: listener #2 [waiting for connection]            
zabbix    6322  6318  0 12:01 ?        00:00:00 sbin/zabbix_agentd: listener #3 [waiting for connection]            
zabbix    6323  6318  0 12:01 ?        00:00:00 sbin/zabbix_agentd: active checks #1 [idle 1 sec]                   
...

La selezione dei processi per nome e utente funziona:

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

Ora rinominiamo l'eseguibile zabbix_agentd in zabbix_agentd_30 e riavviamolo.

ps ora 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/zabbix_agentd.conf               
zabbix    6716  6715  0 12:53 ?        00:00:00 sbin/zabbix_agentd_30: collector [idle 1 sec]                          
zabbix    6717  6715  0 12:53 ?        00:00:00 sbin/zabbix_agentd_30: listener #1 [waiting for connection]            
zabbix    6718  6715  0 12:53 ?        00:00:00 sbin/zabbix_agentd_30: listener #2 [waiting for connection]            
zabbix    6719  6715  0 12:53 ?        00:00:00 sbin/zabbix_agentd_30: listener #3 [waiting for connection]            
zabbix    6720  6715  0 12:53 ?        00:00:00 sbin/zabbix_agentd_30: active checks #1 [idle 1 sec]                   
...

Ora la selezione dei processi per nome e utente produce un risultato errato:

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

Perché una semplice rinomina dell'eseguibile con un nome più lungo porta a un risultato così diverso?

Zabbix agent inizia controllando il nome del processo. Viene aperto il file /proc/<pid>/status e viene controllata la riga Name. Nel nostro caso le righe Name sono:

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

Il nome del processo nel file status viene troncato a 15 caratteri.

Un risultato simile si può vedere con il 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
 ...

Ovviamente, questo non è uguale al valore del parametro name zabbix_agentd_30 del nostro proc.num[]. Non riuscendo a far corrispondere il nome del processo dal file status, Zabbix agent passa al file /proc/<pid>/cmdline.

Come l'agent vede il file "cmdline" può essere illustrato eseguendo il comando

$ for i in 6715 6716 6717 6718 6719 6720; do cat /proc/$i/cmdline | awk '{gsub(/\x0/,"<NUL>"); print};'; done
sbin/zabbix_agentd_30<NUL>-c<NUL>/home/zabbix/zabbix_agentd.conf<NUL>
sbin/zabbix_agentd_30: collector [idle 1 sec]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
sbin/zabbix_agentd_30: listener #1 [waiting for connection]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
sbin/zabbix_agentd_30: listener #2 [waiting for connection]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
sbin/zabbix_agentd_30: listener #3 [waiting for connection]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
sbin/zabbix_agentd_30: active checks #1 [idle 1 sec]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...

Nel nostro caso i file /proc/<pid>/cmdline contengono byte null invisibili e non stampabili, usati per terminare le stringhe nel linguaggio C. In questo esempio i byte null sono mostrati come "<NUL>".

Zabbix agent controlla "cmdline" per il processo principale e prende zabbix_agentd_30, che corrisponde al valore del nostro parametro name zabbix_agentd_30. Quindi il processo principale viene conteggiato dall'item proc.num[zabbix_agentd_30,zabbix].

Quando controlla il processo successivo, l'agent prende zabbix_agentd_30: collector [idle 1 sec] dal file cmdline e questo non corrisponde al nostro parametro name zabbix_agentd_30. Quindi viene conteggiato solo il processo principale, che non modifica la propria command line. Gli altri processi agent modificano la propria command line e vengono ignorati.

Questo esempio mostra che il parametro name non può essere usato in proc.mem[] e proc.num[] per selezionare i processi in questo caso.

Per l'item proc.get[], quando Zabbix agent controlla "cmdline" per il nome del processo, userà solo la parte del nome che inizia dall'ultima barra e termina al primo spazio o ai due punti. Il nome del processo ricevuto dal file cmdline verrà usato solo se il suo inizio corrisponde completamente al nome abbreviato del processo nel file status. L'algoritmo è lo stesso sia per il nome del processo nel filtro sia per quello nell'output JSON.

L'uso del parametro cmdline con un'espressione regolare appropriata produce un risultato corretto:

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

Prestare attenzione quando si usano gli item proc.get[], proc.mem[] e proc.num[] per monitorare programmi che modificano le proprie command line.

Prima di inserire i parametri name e cmdline negli item proc.get[], proc.mem[] e proc.num[], può essere utile testare i parametri usando l'item proc.num[] e il comando ps.

Thread del kernel Linux

I thread non possono essere selezionati con il parametro cmdline negli item proc.get[], proc.mem[] e proc.num[]

Prendiamo come esempio uno dei thread del kernel:

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

Può essere selezionato con il parametro name del processo:

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

Ma la selezione tramite il parametro cmdline del processo non funziona:

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

Il motivo è che Zabbix agent prende l'espressione regolare specificata nel parametro cmdline e la applica al contenuto di /proc/<pid>/cmdline del processo. Per i thread del kernel, i file /proc/<pid>/cmdline sono vuoti. Pertanto, il parametro cmdline non trova mai corrispondenza.

Conteggio dei thread negli item proc.mem[] e proc.num[]

I thread del kernel Linux vengono conteggiati dall'item proc.num[], ma non riportano memoria nell'item proc.mem[]. Per esempio:

$ ps -ef | grep kthreadd
root         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: Cannot get amount of "VmSize" memory.

Ma cosa succede se esiste un processo utente con lo stesso nome di un thread del kernel? In tal caso potrebbe apparire così:

$ ps -ef | grep kthreadd
root         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[] ha conteggiato sia il thread del kernel sia il processo utente. proc.mem[] riporta la memoria solo per il processo utente e considera la memoria del thread del kernel come se fosse 0. Questo è diverso dal caso precedente, in cui veniva riportato ZBX_NOTSUPPORTED.

Prestare attenzione quando si utilizzano gli item proc.mem[] e proc.num[] se il nome del programma coincide con quello di uno dei thread.

Prima di inserire parametri negli item proc.mem[] e proc.num[], si consiglia di testare i parametri usando l'item proc.num[] e il comando ps.