Niektóre programy używają modyfikacji swojej linii poleceń jako metody wyświetlania bieżącej aktywności. Użytkownik może zobaczyć tę aktywność, uruchamiając polecenia ps
i top
. Przykłady takich programów to PostgreSQL, Sendmail, Zabbix.
Zobaczmy przykład z systemu Linux. Załóżmy, że chcemy monitorować liczbę procesów agenta Zabbix.
Polecenie ps
pokazuje interesujące nas procesy jako:
$ 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: 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]
...
Wybór procesów według nazwy i użytkownika spełnia zadanie:
Teraz zmieńmy nazwę pliku wykonywalnego zabbix_agentd
na zabbix_agentd_30
i zrestartujmy go.
ps
teraz pokazuje:
$ 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: 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]
...
Teraz wybór procesów według nazwy i użytkownika daje nieprawidłowy wynik:
Dlaczego prosta zmiana nazwy pliku wykonywalnego na dłuższą nazwę prowadzi do zupełnie innego wyniku?
Agent Zabbix zaczyna od sprawdzenia nazwy procesu. Otwiera plik /proc/<pid>/status
i sprawdza linię Name
. W naszym przypadku linie Name
to:
$ 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
Nazwa procesu w pliku status
jest skrócona do 15 znaków.
Podobny wynik można zobaczyć za pomocą polecenia 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
...
Oczywiście, to nie jest równe naszej wartości parametru name
w proc.num[]
zabbix_agentd_30
. Nie udało się dopasować nazwy procesu z pliku status
, więc agent Zabbix przechodzi do pliku /proc/<pid>/cmdline
.
Jak agent widzi plik "cmdline", można zobrazować, uruchamiając polecenie:
$ 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/ZBXNEXT-1078/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>...
Pliki /proc/<pid>/cmdline
w naszym przypadku zawierają niewidoczne, nieczytelne bajty null, używane do zakończenia ciągów w języku C. W tym przykładzie bajty null są pokazane jako "<NUL>".
Agent Zabbix sprawdza "cmdline" dla głównego procesu i bierze zabbix_agentd_30
, który pasuje do naszej wartości parametru name
zabbix_agentd_30
. Tak więc główny proces jest liczony przez pozycję proc.num[zabbix_agentd_30,zabbix]
.
Podczas sprawdzania kolejnego procesu, agent bierze zabbix_agentd_30: collector [idle 1 sec]
z pliku cmdline
i nie spełnia to naszego parametru name
zabbix_agentd_30
. Tak więc tylko główny proces, który nie modyfikuje swojej linii poleceń, jest liczony. Inne procesy agenta modyfikują swoją linię poleceń i są ignorowane.
Ten przykład pokazuje, że parametr name
nie może być używany w proc.mem[]
i proc.num[]
do wybierania procesów w tym przypadku.
Użycie parametru cmdline
z odpowiednim wyrażeniem regularnym daje prawidłowy wynik:
Bądź ostrożny, używając pozycji proc.mem[]
i proc.num[]
do monitorowania programów, które modyfikują swoje linie poleceń.
Przed wstawieniem parametrów name
i cmdline
do pozycji proc.mem[]
i proc.num[]
, możesz chcieć przetestować parametry za pomocą pozycji proc.num[]
i polecenia ps
.
cmdline
w pozycjach proc.mem[]
i proc.num[]
Weźmy jako przykład jeden z wątków jądra:
Może być wybrany za pomocą parametru name
procesu:
Ale wybór za pomocą parametru cmdline
procesu nie działa:
Powodem jest to, że agent Zabbix bierze wyrażenie regularne określone w parametrze cmdline
i stosuje je do zawartości procesu /proc/<pid>/cmdline
. Dla wątków jądra ich pliki /proc/<pid>/cmdline
są puste. Więc parametr cmdline
nigdy się nie zgadza.
proc.mem[]
i proc.num[]
Wątki jądra Linux są liczone przez pozycję proc.num[]
, ale nie raportują pamięci w pozycji proc.mem[]
. Na przykład:
$ 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.
Ale co się stanie, jeśli istnieje proces użytkownika o tej samej nazwie co wątek jądra? Wtedy może to wyglądać tak:
$ 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[]
policzył zarówno wątek jądra, jak i proces użytkownika. proc.mem[]
raportuje pamięć tylko dla procesu użytkownika i liczy pamięć wątku jądra jakby była równa 0. To różni się od powyższego przypadku, kiedy zgłoszono ZBX_NOTSUPPORTED.
Bądź ostrożny, używając pozycji proc.mem[]
i proc.num[]
jeśli nazwa programu przypadkowo odpowiada jednemu z wątków.
Przed wstawieniem parametrów do pozycji proc.mem[]
i proc.num[]
, możesz chcieć przetestować parametry za pomocą pozycji proc.num[]
i polecenia ps
.