Processes modifying their commandline

    让我们来看一个Linux的例子,假设我们想要监视许多Zabbix代理进程。

    ps 命令显示的进程如下

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

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

    现在让我们将 zabbix_agentd 重命名为 zabbix_agentd_30 并重新启动它。

    ps 现在显示为

    1. $ ps -fu zabbix
    2. UID PID PPID C STIME TTY TIME CMD
    3. ...
    4. zabbix 6715 1 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30 -c /home/zabbix/ZBXNEXT-1078/zabbix_agentd.conf
    5. zabbix 6716 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: collector [idle 1 sec]
    6. zabbix 6717 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: listener #1 [waiting for connection]
    7. zabbix 6718 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: listener #2 [waiting for connection]
    8. zabbix 6719 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: listener #3 [waiting for connection]
    9. zabbix 6720 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: active checks #1 [idle 1 sec]

    现在根据名称和用户选择进程会产生不正确的结果:

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

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

    Zabbix agent 启动时检查进程名字, /proc/<pid>/status 文件是打开的并且检查 Name 行。 我们的例子中 Name 行如下:

    1. $ grep Name /proc/{6715,6716,6717,6718,6719,6720}/status
    2. /proc/6715/status:Name: zabbix_agentd_3
    3. /proc/6716/status:Name: zabbix_agentd_3
    4. /proc/6717/status:Name: zabbix_agentd_3
    5. /proc/6718/status:Name: zabbix_agentd_3
    6. /proc/6719/status:Name: zabbix_agentd_3
    7. /proc/6720/status:Name: zabbix_agentd_3

    ps 命令会产生相似的结果:

    显然, 跟我们的 proc.num[] name 参数值 zabbix_agentd_30并不一样。 Zabbix agent从status 文件中匹配进程名失败后,会转到 /proc/<pid>/cmdline文件。

    agent如何看待“cmdline”文件,可以通过运行一个命令来说明

    1. $ for i in 6715 6716 6717 6718 6719 6720; do cat /proc/$i/cmdline | awk '{gsub(/\x0/,"<NUL>"); print};'; done
    2. sbin/zabbix_agentd_30<NUL>-c<NUL>/home/zabbix/ZBXNEXT-1078/zabbix_agentd.conf<NUL>
    3. sbin/zabbix_agentd_30: collector [idle 1 sec]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
    4. sbin/zabbix_agentd_30: listener #1 [waiting for connection]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
    5. sbin/zabbix_agentd_30: listener #2 [waiting for connection]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
    6. sbin/zabbix_agentd_30: listener #3 [waiting for connection]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...

    /proc/<pid>/cmdline 文件包含在C语言中用于终止字符的隐藏的、 不可显示的空字符 。 这个例子中空字符以 “<NUL>” 形式出现。

    Zabbix agent 检查 “cmdline” ,得到 值, 该值匹配我们的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 参数使用恰当的正则表达式会达到一个正确的结果:

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

    在给 proc.mem[]proc.num[] 监控项使用name and 参数前, 你应该使用 proc.num[] 监控项和 ps 命令测试该参数。

    Linux 内核线程

    ''proc.mem[]'' 和 ''proc.num[]'' 监控项中的 ''cmdline'' 参数不可以使用线程

    让我们以内核线程为例:

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

    可以用进程“名称”参数选择:

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

    但使用进程cmdline 参数就不起作用:

    原因是Zabbix agent采用“cmdline”参数中指定的正则表达式,并将其应用于进程的内容/proc/<pid>/cmdline.对于内核线程的 /proc/<pid>/cmdline 文件是空的, 所以, cmdline 参数不会匹配到。

    ''proc.mem[]'' 和''proc.num[]'' 监控项中的线程计数

    Linux 内核线程通过proc.num[] 监控项计数,但是 proc.mem[] 监控项并不报告内存。 例如:

    1. $ ps -ef | grep kthreadd
    2. root 2 0 0 09:51 ? 00:00:00 [kthreadd]
    1. $ zabbix_get -s localhost -k 'proc.num[kthreadd]'
    2. 1
    1. $ zabbix_get -s localhost -k 'proc.mem[kthreadd]'
    2. ZBX_NOTSUPPORTED: Cannot get amount of "VmSize" memory.

    但是如果用户线程和内核线程名字相同会发生什么呢 ? 可能会是这样:

    1. $ ps -ef | grep kthreadd
    2. root 2 0 0 09:51 ? 00:00:00 [kthreadd]
    3. zabbix 9611 6133 0 17:58 pts/1 00:00:00 ./kthreadd
    1. 4157440

    proc.num[] 计算内核线程和用户进程。proc.mem[] 只计算用户进程内存,如果为0计算内核线程内存。这和上面报告 ZBX_NOTSUPPORTED 的例子不同。

    在给 proc.mem[]proc.num[] 监控项配置参数时, 你应该使用 proc.num[] 监控项 和 ps 命令测试该参数。