htop 中 CPU 频率读取错误的迷之问题

从某一天开始,我也不知道更新了什么, htop 的 CPU 频率突然就变得奇怪了。具体现象是空载时CPU频率基本全部保持在2600Mhz(正好是不开睿频的最高频率)。正常的情况下,空载的时候由于 intel_pstate 的作用,CPU本身的频率应该维持在 800Mhz 左右。一开始我还以为真的是 CPU 频率有问题,怀疑到 CPU 调度器上,但是完成换了几个内核之后状况相同,就排除是调度器的问题了。应该是 htop 读取频率的时候出了问题,而不是 CPU 本身。

除了htop还有什么可以读取CPU频率并与之作对比呢?接着我就去试了试KDE的系统监视器。在KDE的监视器里添加CPU频率的监视条目,可以发现用这个工具读取的频率应该差不多是对的——空载时多数CPU频率处于800Mhz. 然而到现在,一个极为怪诞的现象发生了:

  1. 先开htop,后开系统监视器,htop频率不正确
  2. 先开系统监视器,再开htop,htop频率正确
  3. 在上一步的基础上,关闭系统监视器,htop频率不正确

到这里我真的蒙圈,完全不知道是怎么回事了。到这里只能想想这些软件读取的原理了。遂去看了下 htop 的 issue. 发现读取CPU频率主要是靠两个地方,一个是/proc/cpuinfo,另一个是/sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq. 在我的机器上,前者的频率并不正确,而后者的频率是正确的(可以看下这个 issue),看来只能去看下htop的源码了。

总之在代码里搜来搜去的,这个 commit引起了我的注意。这条commit的大概意思是,先从前者读取CPU频率,若读取速度太慢(判断标准是读取CPU0频率的时间大于500us),则会在后面30次读取中使用后者获取CPU频率。那我们就要测试一下读取的速度了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
❯ time cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq
2859898
3439062
3518834
3357022
3600078
3514387
3590909
3578165
3267212
3537751
3488934
3425438
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq 0.00s user 0.00s system 0% cpu 0.213 total

❯ time grep 'cpu MHz' /proc/cpuinfo
cpu MHz : 2600.000
cpu MHz : 2600.000
cpu MHz : 2600.000
cpu MHz : 2600.000
cpu MHz : 2600.000
cpu MHz : 2600.000
cpu MHz : 2600.000
cpu MHz : 2600.000
cpu MHz : 2600.000
cpu MHz : 2600.000
cpu MHz : 2600.000
cpu MHz : 3600.089
grep 'cpu MHz' /proc/cpuinfo 0.00s user 0.00s system 13% cpu 0.019 total

我也通过 debug 测试了一下,我读取CPU0频率的时间高达16000us, htop 自然会去读后者的频率导致频率显示不正确了。

同时还有另一个发现,也可以解释上面的第二条现象,当用有一个进程在读取/sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq时,再去读这个地方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
❯ time cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq
3295722
3565060
3588762
3109933
3577284
3595533
3566348
3466786
3431875
3391583
3443595
3590967
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq 0.00s user 0.00s system 81% cpu 0.002 total

快多了!这就可以解释上面第二条异常现象的成因了,当有另一个进程在读取的时候,htop测得的延迟小于500us,就获得了正确的频率。

那么到现在解决的方法就昭然若揭了:

  1. 改内核,让延迟降下来
  2. 加入异步,让htop读取成功后再刷新
  3. 让htop开一个子进程去读取频率来降低延迟
  4. 改变阈值让htop不去读取/proc/cpuinfo

很遗憾方法1我是没法做到的,这个延迟似乎涉及psate的实现,是我完全不懂的领域;方法2和方法3估计我写出来提交 PR 也不一定被通过,就不费那个事了;咱就选用最简单的方法,改一下延迟,把500us改成19000us,凑合用得了。这样的后果是 htop 的操作会卡卡的,但是影响也不是很大。


htop 中 CPU 频率读取错误的迷之问题
https://irr.ink/2022/2H3Y57Q/
作者
iR
发布于
2022年9月15日
许可协议