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命令测试参数。