8 Remarques sur la sélection des processus dans les éléments proc.mem et proc.num

Processus modifiant leur ligne de commande

Certains programmes utilisent la modification de leur ligne de commande comme méthode d’affichage de leur activité en cours. Un utilisateur peut voir cette activité en exécutant les commandes ps et top. Parmi ces programmes figurent PostgreSQL, Sendmail, Zabbix.

Voyons un exemple sous Linux. Supposons que nous voulions surveiller un certain nombre de processus d’agent Zabbix.

La commande ps affiche les processus concernés comme suit :

$ 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 sélection des processus par nom et par utilisateur fonctionne :

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

Renommons maintenant l’exécutable zabbix_agentd en zabbix_agentd_30 et redémarrons-le.

ps affiche maintenant :

$ 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]                   
...

À présent, la sélection des processus par nom et par utilisateur produit un résultat incorrect :

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

Pourquoi un simple renommage de l’exécutable avec un nom plus long conduit-il à un résultat si différent ?

L’agent Zabbix commence par vérifier le nom du processus. Le fichier /proc/<pid>/status est ouvert et la ligne Name est vérifiée. Dans notre cas, les lignes Name sont :

$ 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

Le nom du processus dans le fichier status est tronqué à 15 caractères.

Un résultat similaire peut être observé avec la commande 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
 ...

Évidemment, cela n’est pas égal à la valeur du paramètre name de proc.num[], à savoir zabbix_agentd_30. N’ayant pas réussi à faire correspondre le nom du processus à partir du fichier status, l’agent Zabbix se rabat sur le fichier /proc/<pid>/cmdline.

La façon dont l’agent voit le fichier "cmdline" peut être illustrée en exécutant la commande suivante :

$ 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>...

Dans notre cas, les fichiers /proc/<pid>/cmdline contiennent des octets nuls invisibles et non imprimables, utilisés pour terminer les chaînes en langage C. Dans cet exemple, les octets nuls sont affichés sous la forme "<NUL>".

L’agent Zabbix vérifie "cmdline" pour le processus principal et récupère zabbix_agentd_30, ce qui correspond à la valeur de notre paramètre name, zabbix_agentd_30. Ainsi, le processus principal est compté par l’élément proc.num[zabbix_agentd_30,zabbix].

Lors de la vérification du processus suivant, l’agent récupère zabbix_agentd_30: collector [idle 1 sec] à partir du fichier cmdline, et cela ne correspond pas à notre paramètre name zabbix_agentd_30. Ainsi, seul le processus principal, qui ne modifie pas sa ligne de commande, est compté. Les autres processus de l’agent modifient leur ligne de commande et sont ignorés.

Cet exemple montre que le paramètre name ne peut pas être utilisé dans proc.mem[] et proc.num[] pour sélectionner des processus dans ce cas.

Pour l’élément proc.get[], lorsque l’agent Zabbix vérifie "cmdline" pour le nom du processus, il n’utilise que la partie du nom commençant après le dernier slash et jusqu’au premier espace ou au signe deux-points. Le nom du processus reçu à partir du fichier cmdline ne sera utilisé que si son début correspond entièrement au nom de processus raccourci dans le fichier status. L’algorithme est le même à la fois pour le nom du processus dans le filtre et dans la sortie JSON.

L’utilisation du paramètre cmdline avec une expression régulière appropriée produit un résultat correct :

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

Soyez prudent lorsque vous utilisez les éléments proc.get[], proc.mem[] et proc.num[] pour surveiller des programmes qui modifient leurs lignes de commande.

Avant d’utiliser les paramètres name et cmdline dans les éléments proc.get[], proc.mem[] et proc.num[], vous pouvez tester les paramètres à l’aide de l’élément proc.num[] et de la commande ps.

Threads du noyau Linux

Les threads ne peuvent pas être sélectionnés avec le paramètre cmdline dans les éléments proc.get[], proc.mem[] et proc.num[]

Prenons comme exemple l’un des threads du noyau :

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

Il peut être sélectionné avec le paramètre name du processus :

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

Mais la sélection par le paramètre cmdline du processus ne fonctionne pas :

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

La raison est que l’agent Zabbix prend l’expression régulière spécifiée dans le paramètre cmdline et l’applique au contenu de /proc/<pid>/cmdline du processus. Pour les threads du noyau, leurs fichiers /proc/<pid>/cmdline sont vides. Ainsi, le paramètre cmdline ne correspond jamais.

Comptage des threads dans les éléments proc.mem[] et proc.num[]

Les threads du noyau Linux sont comptés par l'élément proc.num[] mais ne signalent pas la mémoire dans l'élément proc.mem[]. Par exemple :

$ 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.

Mais que se passe-t-il si un processus utilisateur porte le même nom qu'un thread du noyau ? Alors cela pourrait ressembler à ceci :

$ 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[] compte à la fois le thread du noyau et le processus utilisateur. proc.mem[] rapporte la mémoire pour le processus utilisateur uniquement et compte la mémoire du thread du noyau comme si elle était à 0. Cela est différent du cas ci-dessus lorsque ZBX_NOTSUPPORTED a été signalé.

Soyez prudent lorsque vous utilisez les éléments proc.mem[] et proc.num[] si le nom du programme correspond à l'un des threads.

Avant de mettre des paramètres dans les éléments proc.mem[] et proc.num[], vous pouvez les tester à l'aide de l'élément proc.num[] et de la commande ps.