某些程序通过修改命令行来显示其当前活动状态。用户可通过运行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_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 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
参数可获得正确结果:
监控会修改命令行的程序时,需谨慎使用proc.mem[]
和proc.num[]
的监控项。
在将name
和cmdline
参数应用于proc.mem[]
和proc.num[]
的监控项前,建议先通过proc.num[]
的监控项和ps
命令测试参数有效性。
cmdline
parameter in proc.mem[]
and proc.num[]
items以某个内核线程为例:
可通过进程name
参数进行选择:
但使用进程cmdline
参数无法匹配:
原因是Zabbix agent会将cmdline
参数中指定的正则表达式应用于进程/proc/<pid>/cmdline
的内容。对于内核线程,其/proc/<pid>/cmdline
文件为空,因此cmdline
参数永远无法匹配。
proc.mem[]
and proc.num[]
itemsLinux内核线程会被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
命令测试参数。