procps-ng 3.3.10 源代码分析 - top (一)

本文的主要目的是分析top的进程CPU占用率是如何计算的。

1 main()

main()的步骤如下.

  • 调用before()做一些初始化工作,如本地化。
  • 调用configs_read()从文件读配置项
  • 调用parse_args()解析命令选项
  • 调用whack_terminal()设置终端
  • 最后,最重要的事,是在while()循环中,完成每一轮采样并显示。
    • 调用frame_make(),计算各个运行数据并显示。
    • 调用函数ioa()。它在内部调用select()作延时操作,参数是变量ts,这是采样的时间间隔。
struct timespec ts;

ts的值是可以通过命令选项-d 指定。如下面的命令选项指定采样间隔为3.5秒。如果没有指定,则使用缺省值1.5秒。

$ top -d 3.5
  • 调用do_key(),看看用户按了什么键。如果按了,则调用相应的处理函数,调整top的输出样式。

2 frame_make()

frame_make()的步骤如下。这里分析的重点是proces_refresh()和window_show()。

  • 调用sysinfo_refresh(),得到全局相关的数据。
  • 调用procs_refresh(),得到进程相关的数据。
  • 调用summary_show(),显示全局的数据。
  • 调用window_show(),在多个子窗口,用不同样式,显示进程相关的数据。这里的分析只有一个窗口。

前面说到,main()的采样间隔由select()的参数指定,但这只是第二轮以后的情况。因为每轮采样计算的是与前值的差值,而第一轮是没有前值的。

top的解决办法是在第一轮之前,调用procs_refresh()设置初值,并调用usleep(),全局常量LIB_USLEEP作为第一轮的采样间隔。这个采样间隔值只有0.15秒,且用户无法改变。

#define LIB_USLEEP  150000  // microseconds

procs_refresh();
usleep(LIB_USLEEP);

所以top每轮的采样间隔如下图所示。第1轮采样间隔是0.15秒,用户不能改变;第2轮以后的采样间隔是1.5秒,用户可以通过命令选项-d改变。

3 top对命令选项 -b -n 1 -p 的处理

  • top可以通过命令行选项 -b指定批处理模式,也就是不接收用户输入;可以通过选项 -n 1指定只进行一轮采样。可以通过选项 -p 只处理一个进程。
$ top -b -n 1 -p 

如前所说,第1轮的采样间隔只有0.15s。

  • 除了R,进程还有几种可能的运行状态,如S,这可能是因为进程调用某些系统函数。比如select()、sleep()都可能将进程置于睡眠状态S,这时进程不分配时间片,也就是不占用cpu。

  • 结合以上两条,如果在这0.15秒内,进程进入睡眠状态,它的cpu占用率就是0。

  • 后面还会看到,top打印cpu占用率的百分数时,只打印一位小数。所以cpu占用率小于0.1%时也会显示0.0%。这样进一步增加了cpu占用率为0的概率。

4 sysinfo_refresh()

sysinfo_refresh()得到本轮采样的全局数据,如内存相关的数据。

  • 调用meminfo()。它从/proc/meminfo读取数据。

5 summary_show()

summary_show()显示本轮采样的全局数据。

  • 调用cpus_refresh()。多cpu核架构下,它从/proc/stat得到所有cpu核的数据。
  • 调用simmary_hlp(),显示全局数据。

你可能感兴趣的:(procps-ng 3.3.10 源代码分析 - top (一))