Это перевод страницы документации с английского языка. Помогите нам сделать его лучше.

10 Заметки по выбору процессов в элементах данных proc.mem и proc.num

Processes modifying their commandline

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

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

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

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

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

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

Zabbix agent начинает с проверки имени процесса. Открывает файл /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
        ...

Очевидно, что этот вывод не идентичен нашему значению zabbix_agentd_30 в параметре name ключа 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/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>...

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

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

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

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

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

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

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

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

Потоки ядра Linux

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

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

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

Его можно выбрать при помощи параметра имя:

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