8 在 proc.mem 和 proc.num items 监控项中选择进程

修改其命令行的进程

一些程序使用修改它们的命令行作为显示当前活动的方用户可以通过运行 pstop 命令来查看活动。这些程序的例子包括 PostgreSQLSendmailZabbix

让我们来看一个 Linux 的例子。假设我们想要监控许多 Zabbix agent 进程。

ps 命令显示的进程如下

  1. $ ps -fu zabbix
  2. UID PID PPID C STIME TTY TIME CMD
  3. ...
  4. zabbix 6318 1 0 12:01 ? 00:00:00 sbin/zabbix_agentd -c /home/zabbix/ZBXNEXT-1078/zabbix_agentd.conf
  5. zabbix 6319 6318 0 12:01 ? 00:00:01 sbin/zabbix_agentd: collector [idle 1 sec]
  6. zabbix 6320 6318 0 12:01 ? 00:00:00 sbin/zabbix_agentd: listener #1 [waiting for connection]
  7. zabbix 6321 6318 0 12:01 ? 00:00:00 sbin/zabbix_agentd: listener #2 [waiting for connection]
  8. zabbix 6322 6318 0 12:01 ? 00:00:00 sbin/zabbix_agentd: listener #3 [waiting for connection]
  9. zabbix 6323 6318 0 12:01 ? 00:00:00 sbin/zabbix_agentd: active checks #1 [idle 1 sec]
  10. ...

通过名称和用户选择进程来完成任务:

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

现在让我们将 zabbix_agentd 重命名为 zabbix_agentd_30 并重新启动它。

ps 现在显示为

  1. $ ps -fu zabbix
  2. UID PID PPID C STIME TTY TIME CMD
  3. ...
  4. zabbix 6715 1 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30 -c /home/zabbix/ZBXNEXT-1078/zabbix_agentd.conf
  5. zabbix 6716 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: collector [idle 1 sec]
  6. zabbix 6717 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: listener #1 [waiting for connection]
  7. zabbix 6718 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: listener #2 [waiting for connection]
  8. zabbix 6719 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: listener #3 [waiting for connection]
  9. zabbix 6720 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: active checks #1 [idle 1 sec]
  10. ...

现在按名称和用户选择进程会产生不正确的结果:

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

为什么将可执行文件重命名为更长的名称会导致完全不同的结果?

Zabbix agent 启动时检查进程名。 /proc/<pid>/status 文件是打开的并检查 Name 行。我们的例子中 Name 行如下:

  1. $ grep Name /proc/{6715,6716,6717,6718,6719,6720}/status
  2. /proc/6715/status:Name: zabbix_agentd_3
  3. /proc/6716/status:Name: zabbix_agentd_3
  4. /proc/6717/status:Name: zabbix_agentd_3
  5. /proc/6718/status:Name: zabbix_agentd_3
  6. /proc/6719/status:Name: zabbix_agentd_3
  7. /proc/6720/status:Name: zabbix_agentd_3

status 文件中的进程名会被截断为15个字符。

ps 命令会产生相似的结果:

  1. $ ps -u zabbix
  2. PID TTY TIME CMD
  3. ...
  4. 6715 ? 00:00:00 zabbix_agentd_3
  5. 6716 ? 00:00:01 zabbix_agentd_3
  6. 6717 ? 00:00:00 zabbix_agentd_3
  7. 6718 ? 00:00:00 zabbix_agentd_3
  8. 6719 ? 00:00:00 zabbix_agentd_3
  9. 6720 ? 00:00:00 zabbix_agentd_3
  10. ...

显然, 跟我们的 proc.num[] name 参数值 zabbix_agentd_30并不一样。Zabbix agent 从 status 文件中匹配进程名失败后,会转到 /proc/<pid>/cmdline 文件。

agent 如何看待 “cmdline” 文件,可以通过运行一个命令来说明

  1. $ for i in 6715 6716 6717 6718 6719 6720; do cat /proc/$i/cmdline | awk '{gsub(/\x0/,"<NUL>"); print};'; done
  2. sbin/zabbix_agentd_30<NUL>-c<NUL>/home/zabbix/ZBXNEXT-1078/zabbix_agentd.conf<NUL>
  3. sbin/zabbix_agentd_30: collector [idle 1 sec]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
  4. sbin/zabbix_agentd_30: listener #1 [waiting for connection]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
  5. sbin/zabbix_agentd_30: listener #2 [waiting for connection]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
  6. sbin/zabbix_agentd_30: listener #3 [waiting for connection]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
  7. sbin/zabbix_agentd_30: active checks #1 [idle 1 sec]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...

/proc/<pid>/cmdline 文件包含在 C 语言用于终止字符的隐藏的、 不可显示的空字符。这个例子中空字符以 “<NUL>” 形式出现。

Zabbix agent 检查 “cmdline” ,得到 zabbix_agentd_30 值,该值匹配我们的 name 参数值 zabbix_agentd_30。因此,主进程会被监控项 proc.num[zabbix_agentd_30,zabbix] 计数。

当检查下一进程时,agent 从 cmdline 文件中得到 zabbix_agentd_30: collector [idle 1 sec] ,但不匹配 name 参数值 zabbix_agentd_30。所以,只有不改变命令行的主进程被计数,其他的 agent 进程改变了命令行而被忽略。

这个例子展示了 name 参数不能用在 proc.mem[]proc.num[] 监控项中来选择进程。

对于 proc.get[] 监控项,当 Zabbix agent 检查 “cmdline” 中的进程名称时,它只会使用从最后一个斜杠开始直到第一个空格或冒号的名称的一部分。 仅当其开头完全匹配 status 文件中的缩短进程名称时,才会使用从 cmdline 文件接收的进程名称。 过滤器中的进程名称和 JSON 输出中的进程名称的算法相同。

cmdline 参数使用恰当的正则表达式会达到一个正确的结。

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

使用 proc.get[]proc.mem[]proc.num[] 监控项来监视修改其命令行的程序时要小心。

在将 namecmdline 参数放入 proc.get[]proc.mem[]proc.num[] 监控项之前,您可能需要使用proc.num[] 监控项和 ps 命令来测试参数。

Linux 内核线程

proc.get[]proc.mem[]proc.num[] 监控项中的 cmdline 参数不可以使用线程

让我们以内核线程为例:

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

可以用进程 name 参数来选择:

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

但使用进程 cmdline 参数就会不起作用:

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

原因是 Zabbix agent 采用 cmdline 参数中指定的正则表达式,并将其应用于进程的内容 /proc/<pid>/cmdline。对于内核线程的 /proc/<pid>/cmdline 文件是空的,所以 cmdline 参数不会匹配到。

proc.mem[]proc.num[] 监控项中的线程计数

Linux 内核线程通过 proc.num[] 监控项计数但是监控项 proc.mem[] 并不报告内存。例如:

  1. $ ps -ef | grep kthreadd
  2. root 2 0 0 09:51 ? 00:00:00 [kthreadd]
  3. $ zabbix_get -s localhost -k 'proc.num[kthreadd]'
  4. 1
  5. $ zabbix_get -s localhost -k 'proc.mem[kthreadd]'
  6. ZBX_NOTSUPPORTED: Cannot get amount of "VmSize" memory.

但是如果用户进程和内核线程名字相同会发生什么呢?可能会是这样:

  1. $ ps -ef | grep kthreadd
  2. root 2 0 0 09:51 ? 00:00:00 [kthreadd]
  3. zabbix 9611 6133 0 17:58 pts/1 00:00:00 ./kthreadd
  4. $ zabbix_get -s localhost -k 'proc.num[kthreadd]'
  5. 2
  6. $ zabbix_get -s localhost -k 'proc.mem[kthreadd]'
  7. 4157440

proc.num[] 计算内核线程和用户进程。proc.mem[] 只计算用户进程内存,如果为0计算内核线程内存。这和上面报告 ZBX_NOTSUPPORTED 的例子不同。

如果程序名恰好匹配其中一个线程,请小心使用 proc.mem[]proc.num[] 监控项。

在给 proc.mem[]proc.num[] 监控项配置参数时,你应该使用, proc.num[] 监控项和 ps 命令测试该参数。