Linux CPU使用率计算
前置 - CPU主频
主频即CPU的时钟频率,计算机的操作在时钟信号的控制下分步执行,每个时钟信号周期完成一部操作,时钟频率的高低一定程度上反应了CPU速度的快慢,影响因素还有很多其它性能指标(缓存、指令集、CPU位数等)
cat /proc/cpuinfo
可看到CPU相关信息,其中包括CPU型号
1 | model name : Intel(R) Xeon(R) Silver 4216 CPU @ 2.10GHz |
如上,2.10GHz,代表此CPU的主时钟脉冲信号的频率为2.10GHz
赫兹(Hertz,符号Hz),是频率的国际单位制单位,表示每一秒周期性事件发生的次数
$kHz = 10^3Hz$
$MHz = 10^6Hz$
$GHz = 10^9Hz$
$THz = 10^{12}Hz$
CPU使用率计算原理
Linux作为一个多任务操作系统,将每个CPU的时间划分为很短的时间片,通过调度器分配各个任务使用,造成多任务同时运行的错觉。
其CPU使用率是通过采样形式统计得到的,采样周期依赖的是Linux时间子系统中的定时器,即Linux内核周期性(像乐谱中的节拍)的会发出timer interrupt
,Linux随之响应并处理一些具体事项。这个节拍
的长度,可通过如下方式获得
1 | localhost ~ # grep CONFIG_HZ= /boot/config-$(uname -r) |
1000
代表了每秒1000次中断,即1ms一次中断
Linux会将瞬时采样的值记录到伪文件系统的文件中/proc/stat
,根据采样情况将相关字段值进行累加,也就是说“瞬时采样时CPU被哪个字段占用,则对应的字段值加1”。
CPU使用率计算方法
计算CPU使用率用到的信息如下:其中cpu
代表的总体情况,cpuN
代表的是单核心情况
1 | localhost ~ # grep ^cpu /proc/stat |
具体的字段值解读,可通过man proc中搜索/proc/stat
章节找到,例如
1 | user (1) Time spent in user mode. |
另外其中还有非常重要的一段话The amount of time, measured in units of USER_HZ (1/100ths of a second on
most architectures, use sysconf(_SC_CLK_TCK) to obtain the right value), that the system ("cpu" line)
or the specific CPU ("cpuN" line) spent in various states:
,提示这里输出的信息用到的单位是USER_HZ
,其值
为1/100
秒,可通过getconf CLK_TCK
命令获得,即1秒100次中断(节拍)。
进一步得出:
- 单位时间内(每秒)CPU花费在
user、nice、system...
的次数总值为100(包括空闲idle) - 用户空间跟内核空间的值不同。
假如user1记录的是第1秒记录的值,user2记录的是第2秒记录的值…则(user2-user1)+(nice2-nice1)+(system2-system1)… = 100
CPU使用率 = $1 - \frac{CPU空闲时间(值)}{CPU总时间(值)}$
即
$1 - \frac{IDLE2 - IDLE1}{取样时间间隔(秒)* 100}$
注意: 实际生产环境(工程实现)中,除关注整体CPU使用率外,还会关注一系列更细粒度的参数:单CPU情况、用户态、内核态、IOWAIT等,条件允许的情况下,最好的处理方式就是将/proc/stat
中的值进行格式化保存到时序数据库中
扩展
参考一
man 7 time
参考二
include/asm-generic/param.h
1 | /* SPDX-License-Identifier: GPL-2.0 */ |
CONFIG_HZ
内核编译时确定的值,定义了Linux内核的时钟频率。
当然基于传统配置(或默认开关),如果有定制要求,可有更多编译选项
1 | CONFIG_HZ_PERIODIC |
USER_HZ
用户空间的应用程序使用的时钟频率,固定值100,通过sysconf(_SC_CLK_TCK)
获取,当然Bash中getconf CLK_TCK
也可获得。
1 | localhost ~ # getconf CLK_TCK |
由此可以看到USER_HZ != CONFIG_HZ,从源码中可以看到系统输出时作了相关转换
fs/proc/stat.c
1 | static int show_stat(struct seq_file *p, void *v) |
jiffy/jiffies
- Linux内核中的时间精度,jiffy值等于
CONFIG_HZ
,也就是说系统调用能识别到的最小值 - jiffies记录系统启动以来经历了多少tick(滴答声,也就是节拍)
1 | localhost ~ # grep -E "^cpu|^jiff" /proc/timer_list |
将jiffies换算后发现跟uptime显示的值不同?!
include/linux/jiffies.h
1 | /* |
可以看到INITIAL_JIFFIES的初始值不为0而是0xfffb6c20
file1.c
1 |
|
1 | test@localhost $ gcc file1.c -o file1 |
实际计算方式
1 | (jiffies - 4294667296)/HZ 即(5084275003-0xfffb6c20)/1000=789607.707秒 |