10 关于在 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文件并检查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文件中的进程名被截断为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文件匹配进程名时,Zabbix agent会转而检查/proc/<pid>/cmdline文件。

通过以下命令可查看agent读取的"cmdline"文件内容:

$ 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文件获取zabbix_agentd_30: collector [idle 1 sec],这与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[] 监控项中报告内存使用情况。例如:

$ 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: 无法获取"VmSize"内存量。

但如果存在与内核线程同名的用户进程会怎样?那么情况可能如下:

$ 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[]仅报告用户进程的内存使用量,并将内核线程的内存视为0。这与之前报告ZBX_NOTSUPPORTED的情况不同。

当程序名称恰好与某个线程匹配时,使用proc.mem[]proc.num[] 监控项需格外谨慎。

在将参数放入proc.mem[]proc.num[] 监控项之前,建议使用proc.num[] 监控项和ps命令测试参数。