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 tales programas incluyen PostgreSQL, Sendmail, Zabbix.
Veamos un ejemplo en Linux. Supongamos que queremos monitorizar un número de procesos del agent 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/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/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 agent de Zabbix comienza comprobando el nombre del proceso. Se abre el archivo /proc/<pid>/status y se comprueba 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_3El nombre del proceso en el archivo status se trunca a 15 caracteres.
Un resultado similar puede verse 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 de nuestro parámetro name de proc.num[] zabbix_agentd_30. Al no poder hacer coincidir el nombre del proceso del archivo status, el agent de Zabbix recurre al archivo /proc/<pid>/cmdline.
Cómo ve el agent el archivo "cmdline" puede ilustrarse 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/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 agent 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 el item proc.num[zabbix_agentd_30,zabbix].
Al comprobar el siguiente proceso, el agent 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, sólo el proceso principal que no modifica su línea de comandos, es contado. Otros procesos del agent modifican su línea de comandos y se ignoran.
Este ejemplo muestra que el parámetro name no puede utilizarse en proc.mem[] y proc.num[] para seleccionar procesos en este caso.
Para el item proc.get[], cuando el agent de Zabbix comprueba "cmdline" para el nombre del proceso, sólo utilizará la parte del nombre que va desde la última barra hasta el primer espacio o signo de dos puntos. El nombre del proceso recibido del archivo cmdline sólo se utilizará si su principio coincide completamente con el nombre del 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.
Utilizar el parámetro cmdline con una expresión regular adecuada produce un resultado correcto:
Tenga cuidado al utilizar los items 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 los items proc.get[], proc.mem[] y proc.num[], puede probar los parámetros utilizando el item 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]'
4157440proc.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.