10 关于在 proc.mem 和 proc.num 监控项中选择进程的注意事项

修改其命令行参数的进程

某些程序通过修改其命令行作为显示信息的一种方式 他们的当前活动。用户可以通过运行ps查看活动 top 命令。此类程序的示例包括 PostgreSQL Sendmail, Zabbix.

让我们来看一个来自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 file。

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会执行 zabbix_agentd_30: collector [idle 1 sec] 来自 cmdline file 和 它不符合我们的name参数zabbix_agentd_30。因此,只有 主进程若未修改其命令行,则会被计入统计。其他 agent进程会修改其命令行参数因而被忽略

此示例表明name参数无法在 proc.mem[]proc.num[] 用于在此情况下选择进程。

使用带有正确正则表达式的cmdline参数可生成 正确结果:

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

在使用proc.mem[]proc.num[] 监控项进行监控时需谨慎 修改其命令行参数的程序

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

Linux 内核线程

Threads cannot be selected with cmdline parameter in 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命令测试参数。