8 Примечания по выбору процессов в элементах данных proc.mem и proc.num

Процессы, изменяющие свою командную строку

Некоторые программы используют изменение своей командной строки как способ отображения текущей активности. Пользователь может увидеть эту активность, выполнив команды ps и top. Примеры таких программ: PostgreSQL, Sendmail, Zabbix.

Рассмотрим пример из Linux. Предположим, мы хотим отслеживать количество процессов агента Zabbix.

Команда ps показывает интересующие нас процессы следующим образом:

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

Выбор процессов по имени и пользователю дает нужный результат:

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

Теперь переименуем исполняемый файл zabbix_agentd в zabbix_agentd_30 и перезапустим его.

Теперь ps показывает:

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

Теперь выбор процессов по имени и пользователю дает неверный результат:

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

Почему простое переименование исполняемого файла в более длинное имя привело к совершенно другому результату?

Агент Zabbix начинает с проверки имени процесса. Открывается файл /proc/<pid>/status и проверяется строка Name. В нашем случае строки Name выглядят так:

$ 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

Имя процесса в файле status усекается до 15 символов.

Похожий результат можно увидеть и с помощью команды 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
 ...

Очевидно, это не равно значению параметра name zabbix_agentd_30 в нашем proc.num[]. Не сумев сопоставить имя процесса из файла status, агент Zabbix переходит к файлу /proc/<pid>/cmdline.

То, как агент видит файл "cmdline", можно проиллюстрировать выполнением команды:

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

Файлы /proc/<pid>/cmdline в нашем случае содержат невидимые, непечатаемые нулевые байты, используемые для завершения строк в языке C. В этом примере нулевые байты показаны как "<NUL>".

Агент Zabbix проверяет "cmdline" для основного процесса и получает zabbix_agentd_30, что соответствует значению нашего параметра name zabbix_agentd_30. Поэтому основной процесс учитывается элементом данных proc.num[zabbix_agentd_30,zabbix].

При проверке следующего процесса агент получает из файла cmdline zabbix_agentd_30: collector [idle 1 sec], и это не соответствует нашему параметру name zabbix_agentd_30. Поэтому учитывается только основной процесс, который не изменяет свою командную строку. Другие процессы агента изменяют свою командную строку и игнорируются.

Этот пример показывает, что параметр name нельзя использовать в proc.mem[] и proc.num[] для выбора процессов в данном случае.

Для элемента данных proc.get[], когда агент Zabbix проверяет "cmdline" для имени процесса, он использует только часть имени, начиная с последнего слеша и до первого пробела или двоеточия. Имя процесса, полученное из файла cmdline, будет использоваться только в том случае, если его начало полностью совпадает с сокращенным именем процесса в файле status. Алгоритм одинаков как для имени процесса в фильтре, так и для имени в выводе JSON.

Использование параметра cmdline с правильным регулярным выражением дает корректный результат:

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

Будьте внимательны при использовании элементов данных proc.get[], proc.mem[] и proc.num[] для мониторинга программ, которые изменяют свои командные строки.

Прежде чем использовать параметры name и cmdline в элементах данных proc.get[], proc.mem[] и proc.num[], может быть полезно проверить эти параметры с помощью элемента данных proc.num[] и команды ps.

Потоки ядра Linux

Потоки нельзя выбрать с помощью параметра cmdline в элементах данных proc.get[], proc.mem[] и proc.num[]

Возьмем в качестве примера один из потоков ядра:

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

Его можно выбрать с помощью параметра процесса name:

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

Но выбор по параметру процесса cmdline не работает:

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

Причина в том, что агент Zabbix берет регулярное выражение, указанное в параметре cmdline, и применяет его к содержимому файла процесса /proc/<pid>/cmdline. Для потоков ядра файлы /proc/<pid>/cmdline пусты. Поэтому параметр cmdline никогда не совпадает.

Подсчет потоков в элементах данных proc.mem[] и proc.num[]

Потоки ядра Linux учитываются элементом данных proc.num[], но не сообщают об использовании памяти в элементе данных proc.mem[]. Например:

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

Но что произойдет, если существует пользовательский процесс с тем же именем, что и у потока ядра? Тогда это может выглядеть так:

$ 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[] учитывает и поток ядра, и пользовательский процесс. proc.mem[] сообщает об использовании памяти только для пользовательского процесса и считает, что использование памяти потоком ядра равно 0. Это отличается от случая выше, когда сообщалось ZBX_NOTSUPPORTED.

Будьте внимательны при использовании элементов данных proc.mem[] и proc.num[], если имя программы случайно совпадает с именем одного из потоков.

Перед тем как указывать параметры в элементах данных proc.mem[] и proc.num[], может быть полезно проверить параметры с помощью элемента данных proc.num[] и команды ps.