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 son 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 los procesos por nombre y usuario hace el trabajo:
Ahora cambiemos el nombre del ejecutable zabbix_agentd
a zabbix_agentd_30
y reiniciémoslo.
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 los procesos por nombre y usuario produce un resultado incorrecto:
¿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 comprueba la línea "Name". En nuestro caso las líneas de 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.
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 name
zabbix_agentd_30
de proc.num[]
. Al no haber podido coincidir con el nombre del proceso del 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 el 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 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 se cuenta para 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 name
zabbix_agentd_30
. Entonces, sólo el proceso principal que no modifica su línea de comando se cuenta. Los 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.
El uso del parámetro cmdline
con una expresión regular adecuada produce un resultado correcto:
Tenga cuidado al utilizar las métricas proc.mem[]
y proc.num[]
para monitorear programas que modifican sus líneas de comando.
Antes de poner los parámetros name
y cmdline
en las métricas proc.mem[]
y proc.num[]
, es posible que desee verificar los parámetros utilizando la métrica proc.num[]
y el comando ps
.
cmdline
en las métricas proc.mem[]
y proc.num[]
Tomemos como ejemplo uno de los subprocesos 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 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.
proc.mem[]
y proc.num[]
Los subprocesos del kernel de Linux se cuentan por la métrica proc.num[]
pero no se informa 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: No se puede obtener la cantidad de memoria "VmSize".
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 las métricas proc.mem[]
y proc.num[]
si el nombre del programa coincide con uno de los hilos.
Antes de poner parámetros en las métricas proc.mem[]
y proc.num[]
, es posible que desee verificar los parámetros utilizando la métrica proc.num[]
y el comando ps
.