https://zhuanlan.zhihu.com/p/33753019
本文主要涉及Linux CPUFreq子系统是什么,为什么需要,怎么用。
并解决在实际测试中遇到的三个问题:
scaling_governor没有userspace的问题。/proc/cpuinfo与cpuinfo_cur_freq显示频率不同。再问为什么之前,可以先看看到底Linux变频到底是什么,可以通过什么手段来看。
本部分使用的操作系统是Fedora27,linux内核版本是4.13。本部分参考参考资料2的博客。
在Linux系统中查看与CPU相关的信息主要有两个方法:
/proc/cpuinfo,比如当前运行频率可以通过|grep MHz查看。但是这个方法在413,414内核变动较大,不推荐通过此方法查看。具体的原因可以见后面的小节。sysfs查看与修改,目录是/sys/devices/system/cpu/cpu*先主要通过第二个方法进行查看:
与CPU相关的sysfs接口均在此目录下,可以通过查看这些文件内容查看CPU相关的信息,例如:
进一步进入cpufreq目录下,该目录下的文件记录与频率相关的内容。
对于fedora系统,无需安装其它工具。如果使用ubuntu系统时,该目录可能为空,需要手动安装cpufrequtils 。
在cpuN(N为具体的第几个CPU)下有关于该CPU的具体参数文件。与频率有关的文件在该目录下的cpufreq文件夹下。fedora系统该文件夹下有如下信息:
cpuinfo_xxx 开头文件代表着cpu硬件上支持的频率cpuinfo_max_freq cpuinfo_min_freq 最大、最小频率cpuinfo_cur_freq 是当前运行的频率(可能也有问题,具体见后面的小节)scaling_xxx开头文件代表可以通过CPUFreq系统用软件进行调节时所支持的频率scaling_available_governors 可供选择的频率调节策略scaling_governors 当前选择的策略scaling_max_freq scaling_min_freq 调节是最大、最小频率scaling_cur_freq 是软件当前指定的频率scaling_driver 调频驱动 (当前intel_pstate)特别注意:可能新版的处理器,新的Linux内核中只有部分接口,且调度也只有powersave和ondemand两种,这是由于使用了p-state driver导致。如何修改见后面小节。
根据以上观察,可以有如下总结:
关于CPUFreq的具体理论,主要参考参考文献1,该参考文献使用的linux内核位2.6版本,该部分以2.6版本为主。
简要来说,变频技术的主要目的是节约功耗。变频技术作为电源管理技术以节能为目的加入linux内核。与之前看的大小核类似,变频技术都是为了 energy efficient computing,为了提高performance per watt。
变频与超频不同的是,超频是指通过提高核心电压等手段让处理器工作在非标准频率下的行为,这往往会造成 CPU 使用寿命缩短以及系统稳定性下降等严重后果。而变频技术是指CPU硬件本身支持在不同的频率下运行,系统在运行过程中可以根据随时可能发生变化的系统负载情况动态在这些不同的运行频率之间进行切换,从而达到对性能和功耗做到二者兼顾的目的。
由于每一个各家实现变频技术的使用方法都不同,故需要按照其实现方法向内核中添加代码,才能支持处理的变频使用。为了兼容各种类型的处理器并最大程度复用代码,故linux中开发了CPUFreq子系统用于实现变频的功能。
所以大思路是要兼容各种处理器,再根据工作负载动态调整处理器的运行的频率。所以可以将这个部分分为两个部分:
从设计角度来说,应该保证二者是清晰的隔离开的并通过规范定义的接口进行通信
CPUFreq内核子系统的设计框架如下图所示:
故cpufreq作为中间层,向上抽象了处理器变频的具体实现,向下分离了变频的策略。将具体的策略和变频的物理上实现进行分离。
这种设计带来的好处是使得 governor 和 CPU 相关的变频驱动程序的开发可以相互独立进行,并在最大限度上实现代码重用,内核开发人员在编写和试验新的 governor 时不会再陷入到某款特定 CPU 的变频技术的硬件实现细节中去,而 CPU 生产厂商在向 Linux 内核中添加支持其特定的 CPU 变频技术的代码时只需提供一个相对来说简单了很多的驱动程序,而不必考虑在各种不同的应用场景中如何选择合适的运行频率这些复杂的问题。
第一部分在实际机器上看的实际sysfs中的文件就是CPUFreq留的接口,可以通过cat对文件进行读取,同时可以用echo向文件写入。在2.6内核中,在选择使用userspace的策略时,向scaling_setspeed中写入指定的频率,可以修改CPU的运行频率。
除了可以使用sysfs中提供的接口,还可以用上文提到的cpufrequtils 工具包。
所以,当选择userspace时,可以每固定时钟间隔根据任务负载对CPU频率进行调节。但是直接在用户态实现这些调节算法有以下两种问题:
由于一个用户态程序很难完整的收集到所有需要的信息,因为这些信息大部分都保存在内核空间,如果想要收集这些系统信息,必然需要进行用户态与内核态之间的数据交互,而频繁的用户态与内核态之间的数据交互又会给系统性能带来负面影响。
所以在2.6内核中提出了ondemand,其是一个在运行在内核态,细粒度时间间隔对系统负载情况采样分析的策略。那么这个表明系统负载的百分比数值是如何得到的呢?
在支持 Intel 最新的 Enhanced Speedstep 技术的 CPU 中,在处理器硬件中直接提供了两个 MSR 寄存器(Model Specific Register)供 ondemand governor 采样分析系统负载情况使用。这两个 MSR 寄存器的名字分别为 IA32_MPERF 和 IA32_APERF,
有了这两个寄存器的存在,再考虑上 CPU 处于 ACPI C0 和处于 ACPI C1、C2、C3 三种状态下的时间比例,也就是 CPU 处于工作状态和休眠状态的时间比例, ondemand governor 就可以准确的计算出 CPU 的负载情况了
实际上支持 Intel Enhanced Speedstep 技术的处理器为用户提供了非常简单的编程接口,对 CPU 运行频率进行设置是通过一个名为 IA32_PERF_CTL 的 MSR 寄存器进行的,另外还有一个名为 IA32_PERF_STATUS 的 MSR 寄存器可供检查 CPU 当前所处的运行频率。当用户需要对 CPU 运行频率进行设置时只需按照 Intel 开发手册的说明向 IA32_PERF_CTL MSR 寄存器中写入规定的数值即可。
这一部分,本来想就是测试一下修改不同核心的频率,然后使其运行在不同的频率下。最后书写测试程序,使线程绑定具体的核心,跑下测试。然而没想到又遇到了很多问题。
governor没有userspace的问题。在两台Macbook Pro和Macbook Air上安装Fedora后并没有看到上面所说的ondemand的调度方法,也没有发现可以通过用户态调整核心运行频率的接口,爬了各种帖子后发现其根本原因出在intel_pstate driver上。(在/sys/devices/system/cpu/cpuX/cpufreq下查看scaling_driver显示为intel_pstate。)
fedora社区讨论地址、archlinux论坛讨论地址,查看之后找到了archlinux里面的一个描述文档
pstate 功率驱动程序,而非下列其他驱动程序。此驱动程序的优先级高于其他驱动程序,并编入内核(而非编译为模块)。此驱动程序将自动用于 Sandy Bridge(以及更新的 CPU)。如果在使用这个驱动的时候遇到问题,建议您在 Grub 的内核参数中将其禁用(即修改 /etc/default/grub 文件,在 GRUB_CMDLINE_LINUX_DEFAULT= 后添加 intel_pstate=disable)。您可以使用与此驱动程序配套的用户空间工具,但这些工具不受您的控制。/sys/devices/system/cpu/intel_pstate 影响,例如:可以通过 # echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo关闭 Intel 睿频加速,从而降低 CPU 的温度。Linux kernel中关于intel_pstate的描述文档:链接 。Intel官方关于此驱动给的Slide:链接。
简单来说,pstat又回去了,结合了变频策略和具体的硬件驱动,为了更好的性能和功耗……可能吧。有机会再详细学习一下pstat。
intel_pstate=disablesudo grub2-mkconfig -o /boot/grub2/grub.cfg #Traditional
sudo grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg #EFI
重新查看/sys/devices/system/cpu/cpuX/cpufreq/scaling_driver已经变为acpi-cpufreq。
本以为大功告成,可以愉快的测试了。结果又出现了更多的问题:
CPU的当前频率!!按照道理来说,第一部分介绍的两种方式查出来的当前频率应该相同。然而,事实并非如此:
/proc/cpuinfo 与 cpuinfo_cur_freq 居然不相符!!!!
发现问题时,我的内核已经更新到Linux4.14了。当然发现与内核版本有关时,已是后话了。
/proc/cpuinfo与cpuinfo_cur_freq中反应的当前的CPU的频率有比较大的出入,下面是在设置调度策略为ondemand时分别监视/proc/cpuinfo中的频率信息与sysfs的cpuinfo_cur_freq中的频率信息。可以发现其中的差异非常大。
当设置调频策略为userspace且设置四个核心为cpuinfo_cur_freq中所示频率时,/proc/cpuinfo中并没有相似的变动。
写了一个创建4个线程并平均分配到四个核心的相同计算任务的测试程序,并设置调频策略为ondemand。可以看到在计算任务重时,虽然两个地方都显示跑到了最高频率,但是最高频率并不相同。
爬了很多帖子,最后在Stack Exchange的这个问题 Why do cpuinfo_cur_freq and /proc/cpuinfo report different numbers? 下找到了线索。其中的一个老哥的回复非常有用,回复内容如下:
Note: Since Kernel 4.13, cat /proc/cpuinfo | grep MHz no longer returns the current clock speed. At the Kernel Bugzilla, they say it's intentional. See /proc/cpuinfo does not update frequency. It's also mentioned here: Power Management Updates Touch Intel P-State & More For Linux 4.13 – Marc Ranolfi Oct 11 '17 at 3:14
在第一个链接描述的现象正是现在我面临的情况:
The 4.13 kernel with intel_pstate=passive (or active) does not appear to correctly update the CPU frequency in /proc/cpuinfo
在该问题的讨论中,提及到4.13之后不再以/proc/cpuinfo读取cpu频率信息,且cpuinfo_cur_freq才是正确的。这也导致一些依赖于/proc/cpuinfo 的软件出现一些问题。
在第二个链接里面提到了在Kernel 4.13的更新中有这样一条新的特性:
x86 systems will no longer try to export their current CPU frequency to /proc/cpuinfo. There's also a way to rework how the CPU frequency is exported via sysfs too, using the registers for computing the current frequency rather than relying upon the CPUFreq driver
好吧,总的来说,就是和Linux内核版本相关,新的内核版本可能取消了向CPUinfo输出实时频率的feature。
而实际测试发现情况并没有这么简单。测试平台CPU信号为Intel haswell架构i5-4258U。
由于Fedora更新比较勤快,我的Fedora系统有三个内核:两个是4.14的,一个是4.13的。
下面测试均开了四个watch窗口,分别监视:/proc/cpuinfo, cpuinfo_cur_freq, scaling_cur_freq, scaling_governor。
首先测试在4.14内核中的情况:
当书写脚本将四个核心均调到性能模式(从按需模式调过来的),可以发现两个现象
/proc/cpuinfo中展现的可能是正确的频率。
然后测试的是powersave模式,如下图所示,有以下发现
/proc/cpuinfo中展现的可能是正确的频率
然后跑自己设置的实验环境,两个900000,两个2401000。这次结果就比较有趣了
cpu0有用,其他的都按照cpu0的频率跑cpuinfo_cur_freq倒是按照了设置来走的,可惜不是实际评论。
在测试一下设置不同的,可以断定所有的cpu均按照cpu0的频率来走了。
再设置回performance时,发现其并没有全部升到最高,可能performance策略不管用了……
总结一下414内核的特点:
/proc/cpuinfo是显示正确频率的地方cpuinfo_cur_freq显示的是错误的,虽然是最接近设置目标的。userspace只能设置统一频率performance不管用切换回4.13内核进行测试,配置测试环境依旧如上相同。这里不单一分析了,直接在最后进行总结。
用户配置模式,这里配置的是2300000, 2300000,1800000,1500000。依旧只按照cpu0进行设置。
powersaving模式:
performance模式:
特意为了检测governor是否也只能统一设置,做了如下实验,得出结论,governor也是统一设置的。
所以4.13内核的总结如下:
/proc/cpuinfo总是显示最高频率,cpuinfo_cur_freq中显示的亦不是正确频率scaling_cur_freq中显示的是当前频率。userspace可以统一设置频率,不能单独设置。performance没有用。scaling_cur_freq为准。上面的测试可以发现,核心频率可以全部修改,不能单独修改?所以到底能不能单独配置每个核心的运行频率呢?
为了找这个问题的答案,爬了很多帖子,在stackoverflow的这个问题下找到了线索:Understanding cpu frequency, thread selection and more,取答案中比较关键的部分翻译如下:
大体来说,cpufreq提供的很多功能并不被Hardware或者Kernel所支持,所以通过cpufreq设置的功能不一定会按照预期运行。例如,如果利用cpufreq为每个cpu设置单独的运行的频率,现实中在超线程的系统,两个CPUs可能会绑定在一起,所以必须有相同的频率。
Intel P-states实际上是提供了一个frequency-voltage对,通过降低cpu运行的电压来降低cpu的运行频率。这些frequency-voltage对是离散的,可能与cpufreq提供的可选频率有出入。
所以从这个答案可以知道第一个原因:
转到Intel的论坛,发现类似的一个帖子:How can I scale only one core's frequency in a multi-core processor? 在这个问题中,McCalpin, John给出了另一个比较详细的解释,原答案可以找原贴看。
首先要了解什么是uncore,其实uncore就是intel用来描述其微处理器上不是un处理器核心core的部分,包括例如L1 L2 cache,ALU之类一堆。处理器运行的频率/电压与uncore有关。
回答的主要内容总结如下
uncore(如i系),只支持设置唯一一个时钟频率。uncore(如E系),虽然支持每个核心不同频率,但是你一旦指定他们运行在不同的频率,uncore中功耗控制单元(PCU)可能会再次改写你的请求,因为PCU可能认为你的设定不合理……MSR IA32_PERF_CTL寄存器,但是这并不意味着硬件会响应你的请求。MSR IA32_PERF_CTL。Haswell 提供了 on-chip voltage regulators ,其允许给不同的核心不同的频率。
所以在我的当前的笔记本环境下跑出不同频率的CPU可能性不大了。本来想着p-state和具体uncore对于变频的处理细节可以暂时不管,可能现在需要继续填坑,详细了解了。