如何正确获取容器的CPU利用率?( 二 )


事实上,Linux 也就是这样来统计系统 cpu 利用率的 。虽然可能会有误差,但作为一项统计数据使用已经是足够了的 。在实现上,Linux 是将所有的瞬时值都累加到某一个数据上的,而不是真的存了很多份的瞬时数据 。
接下来就让我们进入 Linux 来查看它对系统 cpu 利用率统计的具体实现 。
二、top 命令使用数据在哪儿上一节我们说的 Linux 在实现上是将瞬时值都累加到某一个数据上的,这个值是内核通过 /proc/stat 伪文件来对用户态暴露 。Linux 在计算系统 cpu 利用率的时候用的就是它 。
整体上看,top 命令工作的内部细节如下图所示 。

如何正确获取容器的CPU利用率?

文章插图
  • top 命令访问 /proc/stat 获取各项 cpu 利用率使用值
  • 内核调用 stat_open 函数来处理对 /proc/stat 的访问
  • 内核访问的数据来源于 kernel_cpustat 数组,并汇总
  • 打印输出给用户态
接下来我们把每一步都展开来详细看看 。
通过使用 strace 跟踪 top 命令的各种系统调用,可以看的到它对该文件的调用 。
# strace top...openat(AT_FDCWD, "/proc/stat", O_RDONLY) = 4openat(AT_FDCWD, "/proc/2351514/stat", O_RDONLY) = 8openat(AT_FDCWD, "/proc/2393539/stat", O_RDONLY) = 8...
除了 /proc/stat 外,还有各个进程细分的 /proc/{pid}/stat,是用来计算各个进程的 cpu 利用率时使用的 。
内核为各个伪文件都定义了处理函数,/proc/stat 文件的处理方法是 proc_stat_operations 。
//file:fs/proc/stat.cstatic int __init proc_stat_init(void){ proc_create("stat", 0, NULL, &proc_stat_operations); return 0;}static const struct file_operations proc_stat_operations = { .open= stat_open, ...};proc_stat_operations 中包含了该文件时对应的操作方法 。当打开 /proc/stat 文件的时候,stat_open 就会被调用到 。stat_open 依次调用 single_open_size,show_stat 来输出数据内容 。我们来看看它的代码:
//file:fs/proc/stat.cstatic int show_stat(struct seq_file *p, void *v){ u64 user, nice, system, idle, iowait, irq, softirq, steal; for_each_possible_cpu(i) {struct kernel_cpustat *kcs = &kcpustat_cpu(i);user += kcs->cpustat[CPUTIME_USER];nice += kcs->cpustat[CPUTIME_NICE];system += kcs->cpustat[CPUTIME_SYSTEM];idle += get_idle_time(kcs, i);iowait += get_iowait_time(kcs, i);irq += kcs->cpustat[CPUTIME_IRQ];softirq += kcs->cpustat[CPUTIME_SOFTIRQ];... } //转换成节拍数并打印出来 seq_put_decimal_ull(p, "cpu", nsec_to_clock_t(user)); seq_put_decimal_ull(p, " ", nsec_to_clock_t(nice)); seq_put_decimal_ull(p, " ", nsec_to_clock_t(system)); seq_put_decimal_ull(p, " ", nsec_to_clock_t(idle)); seq_put_decimal_ull(p, " ", nsec_to_clock_t(iowait)); seq_put_decimal_ull(p, " ", nsec_to_clock_t(irq)); seq_put_decimal_ull(p, " ", nsec_to_clock_t(softirq)); ...}在上面的代码中,for_each_possible_cpu 是在遍历存储着 cpu 使用率数据的 kcpustat_cpu 变量 。该变量是一个 percpu 变量,它为每一个逻辑核都准备了一个数组元素 。里面存储着当前核所对应各种事件,包括 user、nice、system、idel、iowait、irq、softirq 等 。
在这个循环中,将每一个核的每种使用率都加起来 。最后通过 seq_put_decimal_ull 将这些数据输出出来 。
如何正确获取容器的CPU利用率?

文章插图
注意,在内核中实际每个时间记录的是纳秒数,但是在输出的时候统一都转化成了节拍单位 。至于节拍单位多长,下一节我们介绍 。总之, /proc/stat 的输出是从 kernel_cpustat 这个 percpu 变量中读取出来的 。
我们接着再看看这个变量中的数据是何时加进来的 。
三、统计数据怎么来的前面我们提到内核是以采样的方式来统计 cpu 使用率的 。这个采样周期依赖的是 Linux 时间子系统中的定时器 。
Linux 内核每隔固定周期会发出 timer interrupt (IRQ 0),这有点像乐谱中的节拍的概念 。每隔一段时间,就打出一个拍子,Linux 就响应之并处理一些事情 。
如何正确获取容器的CPU利用率?

文章插图
一个节拍的长度是多长时间,是通过 CONFIG_HZ 来定义的 。它定义的方式是每一秒有几次 timer interrupts 。不同的系统中这个节拍的大小可能不同,通常在 1 ms 到 10 ms 之间 。可以在自己的 Linux config 文件中找到它的配置 。
# grep ^CONFIG_HZ /boot/config-5.4.56.bsk.10-amd64CONFIG_HZ=1000


推荐阅读