8 Hinweise zur Auswahl von Prozessen in proc.mem- und proc.num-Datenpunkten

Prozesse, die ihre Befehlszeile verändern

Einige Programme verwenden das Verändern ihrer Befehlszeile als Methode, um ihre aktuelle Aktivität anzuzeigen. Ein Benutzer kann die Aktivität sehen, indem er die Befehle ps und top ausführt. Beispiele für solche Programme sind PostgreSQL, Sendmail, Zabbix.

Sehen wir uns ein Beispiel unter Linux an. Nehmen wir an, wir möchten eine Anzahl von Zabbix-Agent-Prozessen überwachen.

Der Befehl ps zeigt die relevanten Prozesse wie folgt an:

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

Die Auswahl von Prozessen nach Name und Benutzer erfüllt ihren Zweck:

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

Benennen wir nun die ausführbare Datei zabbix_agentd in zabbix_agentd_30 um und starten sie neu.

ps zeigt nun Folgendes an:

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

Nun liefert die Auswahl von Prozessen nach Name und Benutzer ein falsches Ergebnis:

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

Warum führt ein einfaches Umbenennen der ausführbaren Datei in einen längeren Namen zu einem so anderen Ergebnis?

Der Zabbix-Agent beginnt mit der Prüfung des Prozessnamens. Die Datei /proc/<pid>/status wird geöffnet und die Zeile Name wird geprüft. In unserem Fall lauten die Name-Zeilen:

$ 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

Der Prozessname in der Datei status wird auf 15 Zeichen gekürzt.

Ein ähnliches Ergebnis ist mit dem Befehl ps zu sehen:

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

Offensichtlich ist das nicht gleich unserem name-Parameterwert zabbix_agentd_30 von proc.num[]. Nachdem der Zabbix-Agent den Prozessnamen aus der Datei status nicht abgleichen konnte, greift er auf die Datei /proc/<pid>/cmdline zurück.

Wie der Agent die Datei "cmdline" sieht, lässt sich durch Ausführen des folgenden Befehls veranschaulichen:

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

Die Dateien /proc/<pid>/cmdline enthalten in unserem Fall unsichtbare, nicht druckbare Null-Bytes, die in der Sprache C zum Beenden von Zeichenketten verwendet werden. Die Null-Bytes werden in diesem Beispiel als "<NUL>" dargestellt.

Der Zabbix-Agent prüft "cmdline" für den Hauptprozess und entnimmt zabbix_agentd_30, was mit unserem name-Parameterwert zabbix_agentd_30 übereinstimmt. Daher wird der Hauptprozess vom Datenpunkt proc.num[zabbix_agentd_30,zabbix] gezählt.

Bei der Prüfung des nächsten Prozesses entnimmt der Agent zabbix_agentd_30: collector [idle 1 sec] aus der Datei cmdline, und dies entspricht nicht unserem name-Parameter zabbix_agentd_30. Daher wird nur der Hauptprozess gezählt, der seine Befehlszeile nicht verändert. Andere Agent-Prozesse verändern ihre Befehlszeile und werden ignoriert.

Dieses Beispiel zeigt, dass der Parameter name in diesem Fall nicht in proc.mem[] und proc.num[] zur Auswahl von Prozessen verwendet werden kann.

Beim Datenpunkt proc.get[] verwendet der Zabbix-Agent, wenn er "cmdline" auf den Prozessnamen prüft, nur den Teil des Namens ab dem letzten Schrägstrich bis zum ersten Leerzeichen oder Doppelpunkt. Der aus der Datei cmdline erhaltene Prozessname wird nur verwendet, wenn sein Anfang vollständig mit dem gekürzten Prozessnamen in der Datei status übereinstimmt. Der Algorithmus ist sowohl für den Prozessnamen im Filter als auch in der JSON-Ausgabe derselbe.

Die Verwendung des Parameters cmdline mit einem geeigneten regulären Ausdruck liefert ein korrektes Ergebnis:

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

Seien Sie vorsichtig bei der Verwendung von proc.get[]-, proc.mem[]- und proc.num[]-Datenpunkten zur Überwachung von Programmen, die ihre Befehlszeilen verändern.

Bevor Sie die Parameter name und cmdline in proc.get[]-, proc.mem[]- und proc.num[]-Datenpunkten verwenden, sollten Sie die Parameter möglicherweise mit dem Datenpunkt proc.num[] und dem Befehl ps testen.

Linux-Kernel-Threads

Threads können in proc.get[]-, proc.mem[]- und proc.num[]-Datenpunkten nicht mit dem Parameter cmdline ausgewählt werden

Nehmen wir als Beispiel einen der Kernel-Threads:

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

Er kann mit dem Prozessparameter name ausgewählt werden:

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

Die Auswahl über den Prozessparameter cmdline funktioniert jedoch nicht:

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

Der Grund dafür ist, dass der Zabbix Agent den im Parameter cmdline angegebenen regulären Ausdruck verwendet und ihn auf den Inhalt von /proc/<pid>/cmdline des Prozesses anwendet. Bei Kernel-Threads sind ihre Dateien /proc/<pid>/cmdline leer. Daher liefert der Parameter cmdline niemals eine Übereinstimmung.

Zählung von Threads in proc.mem[]- und proc.num[]-Datenpunkten

Linux-Kernel-Threads werden vom proc.num[]-Datenpunkt gezählt, melden jedoch keinen Speicher im proc.mem[]-Datenpunkt. Zum Beispiel:

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

Aber was passiert, wenn es einen Benutzerprozess mit demselben Namen wie ein Kernel-Thread gibt? Dann könnte es so aussehen:

$ 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[] zählte sowohl den Kernel-Thread als auch den Benutzerprozess. proc.mem[] meldet Speicher nur für den Benutzerprozess und zählt den Speicher des Kernel-Threads so, als wäre er 0. Dies unterscheidet sich von dem oben beschriebenen Fall, in dem ZBX_NOTSUPPORTED gemeldet wurde.

Seien Sie vorsichtig bei der Verwendung von proc.mem[]- und proc.num[]-Datenpunkten, wenn der Programmname zufällig mit einem der Threads übereinstimmt.

Bevor Sie Parameter in proc.mem[]- und proc.num[]-Datenpunkte eintragen, sollten Sie die Parameter möglicherweise mit dem proc.num[]-Datenpunkt und dem Befehl ps testen.