8 关于在 proc.mem 和 proc.num 中选择进程的说明 监控项

修改其命令行参数的进程

一些程序通过修改其命令行作为显示当前活动的方法。用户可以通过运行 pstop 命令查看活动。此类程序的示例包括 PostgreSQLSendmailZabbix

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

ps 命令显示感兴趣的进程如下:

$ 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]                   
       ...

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

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

现在我们将 zabbix_agentd 可执行文件重命名为 zabbix_agentd_30 并重新启动它。

ps 现在显示如下:

$ 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]                   
       ...

现在通过名称和用户选择进程会产生错误的结果:

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

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

Zabbix agent 从检查进程名称开始。打开 /proc/<pid>/status file 并检查 Name 行。在我们的案例中,Name 行如下:

$ 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

status file 中的进程名称被截断为 15 个字符。

使用 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
        ...

显然,这与我们的 proc.num[] name 参数值 zabbix_agentd_30 不相等。由于未能从 status file 匹配进程名称,Zabbix agent 转向 /proc/<pid>/cmdline 文件。

如何通过 agent 查看 "cmdline" file 可以通过运行以下命令来说明:

$ 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>...

在我们的案例中,/proc/<pid>/cmdline 文件包含不可见的、不可打印的空字节,用于在 C 语言中终止字符串。空字节在此示例中显示为 "<NUL>"。

Zabbix agent 检查主进程的 "cmdline" 并提取 zabbix_agentd_30,这与我们的 name 参数值 zabbix_agentd_30 匹配。因此,主进程通过 监控项 proc.num[zabbix_agentd_30,zabbix] 被计数。

在检查下一个进程时,agent 从 cmdline file 提取 zabbix_agentd_30: collector [idle 1 sec],但它不符合我们的 name 参数值 zabbix_agentd_30。因此,只有未修改其命令行的主进程被计数。其他 agent 进程修改了其命令行并被忽略。

此示例表明,在此情况下,不能在 proc.mem[]proc.num[] 中使用 name 参数来选择进程。

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

使用 cmdline 参数和适当的正则表达式可以产生正确的结果:

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

在使用 proc.get[]proc.mem[]proc.num[] 监控项 监控修改其命令行的程序时,请务必小心。

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

Linux 内核线程

Threads cannot be selected with cmdline parameter in proc.get[], proc.mem[] and proc.num[] items

我们以其中一个内核线程为例:

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

可以使用进程 name 参数进行选择:

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

但是使用进程 cmdline 参数进行选择则不起作用:

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

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

Counting of threads in proc.mem[] and proc.num[] items

Linux 内核线程由 proc.num[] 监控项 进行计数,但在 proc.mem[] 监控项 中不报告 memory。例如:

$ 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.

但是如果存在一个与内核线程同名的用户进程会发生什么?这可能如下所示:

$ 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[] 同时计数了内核线程和用户进程。
proc.mem[] 仅对用户进程报告 memory,并将内核线程的 memory 计算为 0。这与上面报告 ZBX_NOTSUPPORTED 的情况不同。

当程序名称恰好与线程名称匹配时,在使用 proc.mem[]proc.num[] 监控项 时请小心。

在将参数放入 proc.mem[]proc.num[] 监控项 之前,您可能希望使用 proc.num[] 监控项 和 ps 命令测试这些参数。