Algunos programas utilizan la modificación de su línea de comandos como un método para mostrar su actividad actual. Un usuario puede ver la actividad ejecutando los comandos ps
y top
. Ejemplos de tales programas incluyen PostgreSQL, Sendmail, Zabbix.
Veamos un ejemplo en Linux. Supongamos que queremos monitorizar un número de procesos del agente de Zabbix.
El comando ps
muestra los procesos de interés como
$ 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]
...
Seleccionar procesos por nombre y usuario hace el trabajo:
Ahora renombremos el ejecutable zabbix_agentd
a zabbix_agentd_30
y reiniciémoslo.
ps
ahora muestra
$ 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]
...
Ahora, seleccionar procesos por nombre y usuario produce un resultado incorrecto:
¿Por qué un simple cambio de nombre del ejecutable a uno más largo conduce a un resultado tan diferente?
El agente de Zabbix comienza comprobando el nombre del proceso. Se abre el archivo /proc/<pid>/status
y se verifica la línea Name
. En nuestro caso, las líneas Name
son:
$ 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
El nombre del proceso en el archivo status
se trunca a 15 caracteres.
Un resultado similar se puede ver con el comando 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
...
Obviamente, eso no es igual al valor del parámetro name
de nuestro proc.num[]
zabbix_agentd_30
. Al no poder coincidir el nombre del proceso del archivo status
, el agente de Zabbix recurre al archivo /proc/<pid>/cmdline
.
Cómo ve el agente 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: 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 bytes nulos invisibles y no imprimibles, utilizados para terminar cadenas en el lenguaje C. Los bytes nulos se muestran como "<NUL>" en este ejemplo.
El agente de Zabbix comprueba "cmdline" para el proceso principal y toma un zabbix_agentd_30
, que coincide con el valor de nuestro parámetro name
zabbix_agentd_30
. Por lo tanto, 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 coincide con nuestro parámetro name
zabbix_agentd_30
. Por lo tanto, solo el proceso principal que no modifica su línea de comandos es contado. Otros procesos del agente modifican su línea de comandos y son ignorados.
Este ejemplo muestra que el parámetro name
no puede usarse en proc.mem[]
y proc.num[]
para seleccionar procesos en este caso.
Para la métrica proc.get[]
, cuando el agente de Zabbix comprueba "cmdline" para el nombre del proceso, solo usará parte del nombre comenzando desde la última barra y hasta el primer espacio o signo de dos puntos. El nombre del proceso recibido del archivo cmdline solo se usará si su inicio coincide completamente con el nombre de proceso acortado en el archivo status
. El algoritmo es el mismo tanto para el nombre del proceso en el filtro como en la salida JSON.
Usar el parámetro cmdline
con una expresión regular adecuada produce un resultado correcto:
Tenga cuidado al usar las métricas proc.get[]
, proc.mem[]
y proc.num[]
para monitorizar programas que modifican sus líneas de comandos.
Antes de poner los parámetros name
y cmdline
en las métricas proc.get[]
, proc.mem[]
y proc.num[]
, puede que desee probar los parámetros usando la métrica proc.num[]
y el comando ps
.
cmdline
en las métricas proc.get[]
, proc.mem[]
y proc.num[]
Tomemos como ejemplo uno de los hilos del kernel:
Se puede seleccionar con el parámetro name
del proceso:
Pero la selección por el parámetro cmdline
del proceso no funciona:
La razón es que el agente Zabbix toma la expresión regular especificada en el parámetro cmdline
y la aplica al contenido del archivo /proc/<pid>/cmdline
del proceso. Para los hilos del kernel, sus archivos /proc/<pid>/cmdline
están vacíos. Por lo tanto, el parámetro cmdline
nunca coincide.
proc.mem[]
y proc.num[]
Los hilos del kernel de Linux son contados por la métrica proc.num[]
pero no informan 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 kernel? 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 kernel como el proceso de usuario. proc.mem[]
informa la memoria solo para el proceso de usuario y cuenta la memoria del hilo del kernel como si fuera 0. Esto es diferente del caso anterior cuando se informó ZBX_NOTSUPPORTED.
Tenga cuidado al usar las métricas proc.mem[]
y proc.num[]
si el nombre del programa coincide con el de algún hilo.
Antes de poner parámetros en las métricas proc.mem[]
y proc.num[]
, puede probar los parámetros usando la métrica proc.num[]
y el comando ps
.