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:
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:
¿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:
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
.
cmdline
en los elementos proc.get[]
, proc.mem[]
y proc.num[]
Tomemos como ejemplo uno de los subprocesos del kernel:
Se puede seleccionar con el parámetro nombre
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 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
.