9 Notas sobre la selección de procesos en los elementos proc.mem y proc.num

Procesos modificando su línea de comando

Algunos programas utilizan la modificación de su línea de comandos como método para mostrar su actividad actual. Un usuario puede ver la actividad ejecutando los comandos ps y top. Ejemplos de dichos programas incluyen PostgreSQL, Sendmail, Zabbix.

Veamos un ejemplo de Linux. Supongamos que queremos monitorear un número de procesos del agente Zabbix.

El comando ps muestra procesos de interés como

$ ps -fu zabbix
       UID PID PPID C TIEMPO TTY TIEMPO 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]
       ...

Seleccionar procesos por nombre y usuario hace el trabajo:

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

Ahora cambiemos el nombre del ejecutable zabbix_agentd a zabbix_agentd_30 y reinícielo.

ps ahora muestra

$ ps -fu zabbix
       UID PID PPID C TIEMPO TTY TIEMPO 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]
       ...

Ahora seleccionar procesos por nombre y usuario produce un resultado incorrecto:

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

¿Por qué un simple cambio de nombre del ejecutable a un nombre más largo conduce a un resultado bastante diferente?

El agente Zabbix comienza verificando el nombre del proceso. /proc/<pid>/estado Se abre el archivo y se verifica la línea "Nombre". En nuestro caso el Nombre de las líneas son:

$ grep Name /proc/{6715,6716,6717,6718,6719,6720}/status
       /proc/6715/status:Nombre: zabbix_agentd_3
       /proc/6716/status:Nombre: zabbix_agentd_3
       /proc/6717/status:Nombre: zabbix_agentd_3
       /proc/6718/status:Nombre: zabbix_agentd_3
       /proc/6719/status:Nombre: zabbix_agentd_3
       /proc/6720/status:Nombre: zabbix_agentd_3

El nombre del proceso en el archivo "estado" se trunca a 15 caracteres.

Se puede ver un resultado similar con el comando ps:

$ ps -u zabbix
         PID TTY TIEMPO 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
        ...

Obviamente, eso no es igual al valor de nuestro parámetro proc.num[] name zabbix_agentd_30. Al no haber podido coincidir con el nombre del proceso de archivo status que el agente Zabbix convierte en el archivo /proc/<pid>/cmdline.

La forma en que el agente ve el archivo "cmdline" se puede ilustrar ejecutando un comando

$ 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: recopilador [inactivo 1 segundo]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><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>...

Los archivos /proc/<pid>/cmdline en nuestro caso contienen archivos invisibles y bytes nulos no imprimibles, utilizados para terminar cadenas en lenguaje C. Los bytes nulos se muestran como "<NUL>" en este ejemplo.

El agente de Zabbix comprueba "cmdline" para el proceso principal y toma zabbix_agentd_30, que coincide con el valor de nuestro parámetro name zabbix_agentd_30. Entonces, el proceso principal es contado por la métrica proc.num[zabbix_agentd_30,zabbix].

Al comprobar el siguiente proceso, el agente toma zabbix_agentd_30: collector [idle 1 sec] del archivo cmdline y no cumple con nuestro parámetro nombre zabbix_agentd_30. Entonces, sólo el proceso principal que no modifica su línea de comando se cuenta. Otros procesos del agente modifican su línea de comando y son ignorados.

Este ejemplo muestra que el parámetro name no se puede utilizar en proc.mem[] y proc.num[] para seleccionar procesos en este caso.

Para el elemento proc.get[], cuando el agente Zabbix busca en "cmdline" el nombre del proceso, solo usará parte del nombre comenzando desde la última barra diagonal y hasta el primer espacio o signo de dos puntos. El nombre del proceso recibido del archivo cmdline solo se utilizará si su comienzo coincide completamente con el nombre del proceso abreviado en el archivo "status". El algoritmo es el mismo tanto para el nombre del proceso en el filtro como en la salida JSON.

El uso del parámetro cmdline con una expresión regular adecuada produce un resultado correcto:

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

Tenga cuidado al utilizar los elementos proc.get[], proc.mem[] y proc.num[] para monitorear programas que modifican sus líneas de comando.

Antes de colocar los parámetros name y cmdline en las métricas proc.get[], proc.mem[] y proc.num[], es posible que quiera probar los parámetros usando la métrica proc.num[] y el comando ps.

Hilos del kernel de Linux

Los subprocesos no se pueden seleccionar con el parámetro cmdline en los elementos proc.get[], proc.mem[] y proc.num[]

Tomemos como ejemplo uno de los subprocesos del kernel:

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

Se puede seleccionar con el parámetro nombre del proceso:

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

Pero la selección por el parámetro cmdline del proceso no funciona:

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

La razón es que el agente Zabbix toma la expresión regular especificada en el parámetro cmdline y lo aplica al contenido del proceso /proc/<pid>/cmdline. Para los subprocesos del kernel, sus archivos /proc/<pid>/cmdline están vacíos. Entonces, el parámetro cmdline nunca coincide.

Recuento de subprocesos en los elementos proc.mem[] y proc.num[]

Los subprocesos del kernel de Linux se cuentan por el elemento proc.num[] pero no se informan la memoria en la métrica proc.mem[]. Por ejemplo:

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

Pero, ¿qué sucede si hay un proceso de usuario con el mismo nombre que un hilo del núcleo? Entonces podría verse así:

$ 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[] contó tanto el hilo del núcleo como el proceso del usuario. proc.mem[] informa memoria solo para el proceso del usuario y cuenta la memoria del hilo del kernel como si fuera 0. Esto es diferente del caso de arriba cuando se informó ZBX_NOTSUPPORTED.

Tenga cuidado al utilizar los elementos proc.mem[] y proc.num[] si el nombre del programa coincide con uno de los hilos.

Antes de poner parámetros en los elementos proc.mem[] y proc.num[], es posible que desee probar los parámetros utilizando la métrica proc.num[] y el comando ps.