本系列文章将结合近年来不断在各种硬件(包括CPU、芯片组、PCIExpress等各种最新总线标准以及外设)上新增的节能技术,从Linux®2.6内核及整个softwarestack(包括kernel、middleware以及各种用户态utility)如何添加对这些创新的节能技术的支持这一角度,为读者介绍Linux操作系统近几年来在电源管理方面所取得的长足进步以及未来的发展方向。
作为本系列文章的开篇之作,首先要向大家介绍的是cpufreq,它是Linux2.6内核为了更好的支持近年来在各款主流CPU处理器中出现的变频技术而新增的一个内核子系统。
Cpufreq的由来
随着energyefficientcomputing和performanceperwatt等概念的推广以及高级配置与电源接口ACPI(AdvancedConfigurationandPowerInterface)标准的发展,目前市场上的主流CPU都提供了对变频(frequencyscaling)技术的支持。例如Intel®处理器所支持的EnhancedSpeedStep®技术和AMD®处理器所支持的PowerNow!®技术,另外像最新的PowerPC®、ARM®、SPARC®和SuperH®等处理器中也提供了类似的支持。参考资料中列出了当前Linux2.6内核所支持的具备变频技术的处理器。需要注意的是,这里要讨论的变频技术与大家以前所熟知的超频是两个不同的概念。超频是指通过提高核心电压等手段让处理器工作在非标准频率下的行为,这往往会造成CPU使用寿命缩短以及系统稳定性下降等严重后果。而变频技术是指CPU硬件本身支持在不同的频率下运行,系统在运行过程中可以根据随时可能发生变化的系统负载情况动态在这些不同的运行频率之间进行切换,从而达到对性能和功耗做到二者兼顾的目的。
虽然多个处理器生产厂家都提供了对变频技术的支持,但是其硬件实现和使用方法必然存在着细微甚至巨大的差别。这就使得每个处理器生产厂家都需要按照其特殊的硬件实现和使用方法向内核中添加代码,从而让自己产品中的变频技术在Linux中得到支持和使用。然而,这种内核开发模式所导致的后果是各个厂家的实现代码散落在Linux内核代码树的各个角落里,各种不同的实现之间没有任何代码是共享的,这给内核的维护以及将来添加对新的产品的支持都带来了巨大的开销,并直接导致了cpufreq内核子系统的诞生。实际上,正如前文所说,发明变频技术的目的是为了能够让系统在运行过程中随时根据系统负载的变化动态调整CPU的运行频率。这件事情可以分为两个部分,一部分是“做什么”的问题,另一部分是“怎么做”的问题。“做什么”是指如何根据系统负载的动态变化挑选出CPU合适的运行频率,而“怎么做”就是要按照选定的运行频率在选定的时间对CPU进行设置,使之真正工作在这一频率上。这也就是我们在软件设计中经常会遇到的机制mechanism与策略policy的问题,而设计良好的软件会在架构上保证二者是被清晰的隔离开的并通过规范定义的接口进行通信。
Cpufreq的设计和使用
为了解决前文所提到的问题,一个新的内核子系统——cpufreq应运而生了。Cpufreq为在Linux内核中更好的支持不同CPU的变频技术提供了一个统一的设计框架,其软件结构如图1所示。
图 1. Cpufreq 的软件结构
如图 1 所示,cpufreq 在设计上主要分为以下三个模块:
Cpufreq模块(cpufreqmodule)对如何在底层控制各种不同CPU所支持的变频技术以及如何在上层根据系统负载动态选择合适的运行频率进行了封装和抽象,并在二者之间定义了清晰的接口,从而在设计上完成了前文所提到的对mechanism与policy的分离。
在cpufreq模块的底层,各个CPU生产厂商只需根据其变频技术的硬件实现和使用方法提供与其CPU相关的变频驱动程序(CPU-specificdrivers),例如Intel需要提供支持EnhancedSpeedStep技术的CPU驱动程序,而AMD则需要提供支持PowerNow!技术的CPU驱动程序。
在cpufreq模块的上层,governor作为选择合适的目标运行频率的决策者,根据一定的标准在适当的时刻选择出CPU适合的运行频率,并通过cpufreq模块定义的接口操作底层与CPU相关的变频驱动程序,将CPU设置运行在选定的运行频率上。目前最新的Linux内核中提供了performance、powersave、userspace、conservative和ondemand五种governors供用户选择使用,它们在选择CPU合适的运行频率时使用的是各自不同的标准并分别适用于不同的应用场景。用户在同一时间只能选择其中一个governor使用,但是可以在系统运行过程中根据应用需求的变化而切换使用另一个governor。
这种设计带来的好处是使得governor和CPU相关的变频驱动程序的开发可以相互独立进行,并在最大限度上实现代码重用,内核开发人员在编写和试验新的governor时不会再陷入到某款特定CPU的变频技术的硬件实现细节中去,而CPU生产厂商在向Linux内核中添加支持其特定的CPU变频技术的代码时只需提供一个相对来说简单了很多的驱动程序,而不必考虑在各种不同的应用场景中如何选择合适的运行频率这些复杂的问题。
内核中的cpufreq子系统通过sysfs文件系统向上层应用提供了用户接口,对于系统中的每一个CPU而言,其cpufreq的sysfs用户接口位于/sys/devices/system/cpu/cpuX/cpufreq/目录下,其中X代表processorid,与/proc/cpuinfo中的信息相对应。以cpu0为例,用户一般会在该目录下观察到以下文件:
$ls -f /sys/devices/system/cpu/cpu0/cpufreq/
affected_cpus
cpuinfo_cur_freq
cpuinfo_max_freq
cpuinfo_min_freq
ondemand/
scaling_available_frequencies
scaling_available_governors
scaling_cur_freq
scaling_driver
scaling_governor
scaling_max_freq
scaling_min_freq
stats/
这其中的所有可读文件都可以使用cat命令进行读操作,另外所有可写文件都可以使用echo命令进行写操作。其中cpuinfo_max_freq和cpuinfo_min_freq分别给出了CPU硬件所支持的最高运行频率及最低运行频率,cpuinfo_cur_freq则会从CPU硬件寄存器中读取CPU当前所处的运行频率。虽然CPU硬件支持多种不同的运行频率,但是在有些场合下用户可以只选择使用其中的一个子集,这种控制是通过scaling_max_freq和scaling_min_freq进行的。Governor在选择合适的运行频率时只会在scaling_max_freq和scaling_min_freq所确定的频率范围内进行选择,这也就是scaling_available_frequencies所显示的内容。与cpuinfo_cur_freq不同,scaling_cur_freq返回的是cpufreq模块缓存的CPU当前运行频率,而不会对CPU硬件寄存器进行检查。scaling_available_governors会告诉用户当前有哪些governors可供用户使用,而scaling_driver则会显示该CPU所使用的变频驱动程序。Stats目录下给出了对CPU各种运行频率的使用统计情况,例如CPU在各种频率下的运行时间以及在各种频率之间的变频次数。Ondemand目录则与ondemandgovernor相关,在后文会进行相应的介绍。
通过以上的介绍,大家对如何使用cpufreq通过sysfs提供的用户接口已经有了大致的了解,但是对于绝大部分用户而言,逐一操作这些文件既费力又耗时。因此Dominik等人开发了cpufrequtils工具包[2],为用户提供了更加简便的对内核cpufreq子系统的操作接口。通过cpufreq-info的输出,读者可以很清楚的看到刚刚在上面介绍过的/sys/devices/system/cpu/cpuX/cpufreq/目录下各个文件的内容。
$cpufreq-info
cpufrequtils002:cpufreq-info(C)DominikBrodowski2004-2006
Reporterrorsandbugstolinux@brodo.de,please.
analyzingCPU0:
driver:acpi-cpufreq
CPUswhichneedtoswitchfrequencyatthesametime:01
hardwarelimits:1000MHz-1.67GHz
availablefrequencysteps:1.67GHz,1.33GHz,1000MHz
availablecpufreqgovernors:userspace,conservative,ondemand,powersave,performance
currentpolicy:frequencyshouldbewithin1000MHzand1.67GHz.
Thegovernor"ondemand"maydecidewhichspeedtouse
withinthisrange.
currentCPUfrequencyis1000MHz.
analyzingCPU1:
driver:acpi-cpufreq
CPUswhichneedtoswitchfrequencyatthesametime:01
hardwarelimits:1000MHz-1.67GHz
availablefrequencysteps:1.67GHz,1.33GHz,1000MHz
availablecpufreqgovernors:userspace,conservative,ondemand,powersave,performance
currentpolicy:frequencyshouldbewithin1000MHzand1.67GHz.
Thegovernor"ondemand"maydecidewhichspeedtouse
withinthisrange.
currentCPUfrequencyis1000MHz.
Ondemandgovernor的由来及其实现:
刚刚我们在cpufreq-info的输出中可以看到cpufreq子系统一共提供了五种governors供用户选择使用,它们分别是userspace,conservative,ondemand,powersave和performance。在最新的内核中如果用户不进行额外设置的话,ondemand会被作为默认的governor使用。为了理解是什么原因造成了这种现状,我们在这里带领读者回顾一下cpufreq子系统中的governor在内核中的开发历史。
Cpufreq作为一个子系统最早被加入到Linux内核中时只配备了三个governors,分别是performance、powersave和userspace。当用户选择使用performancegovernor时,CPU会固定工作在其支持的最高运行频率上;当用户选择使用powersavegovernor时,CPU会固定工作在其支持的最低运行频率上。因此这两种governors都属于静态governor,即在使用它们时CPU的运行频率不会根据系统运行时负载的变化动态作出调整。这两种governors对应的是两种极端的应用场景,使用performancegovernor体现的是对系统高性能的最大追求,而使用powersavegovernor则是对系统低功耗的最大追求。虽然这两种应用需求确实存在,但大多数用户在大部分时间里需要的是更加灵活的变频策略。最早的cpufreq子系统通过userspacegovernor为用户提供了这种灵活性。正如它的名字一样,使用userspacegovernor时,系统将变频策略的决策权交给了用户态应用程序,并提供了相应的接口供用户态应用程序调节CPU运行频率使用。通过使用cpufrequtils工具包中的cpufreq-set将userspace设置为cpufreq子系统所使用的governor后,我们可以看到与之前相比在/sys/devices/system/cpu/cpuX/cpufreq/目录下多出了一个名为scaling_setspeed的文件,这正是userspacegovernor所提供的特殊用户接口。用户可以通过向该文件写入任何一个scaling_available_frequencies中所支持的运行频率,从而将CPU设置在该频率下运行。
#cpufreq-set-guserspace
#cat cpuinfo_cur_freq
1000000
#cat scaling_available_frequencies
166700013330001000000
#echo 1333000>scaling_setspeed
#cat cpuinfo_cur_freq
1333000
(注此处获取和设置相关的文件时候需linux 内核支持,否则很容易出现echo 失败的情况)
刚刚提到在使用userspacegovernor时,系统将变频策略的决策权交给了用户态应用程序。该用户态应用程序一般是一个daemon程序,每隔一定的时间间隔收集一次系统信息并根据系统的负载情况使用userspacegovernor提供的scaling_setspeed接口动态调整CPU的运行频率。作为这个daemon程序,当时在几个主要的Linux发行版中使用的一般是powersaved或者cpuspeed。这两个daemon程序一般每隔几秒钟统计一次CPU在这个采样周期内的负载情况,并根据统计结果调整CPU的运行频率。这种userspacegovernor加用户态daemon程序的变频方法虽然为用户提供了一定的灵活性,但通过开源社区的广泛使用所得到的意见反馈逐渐暴露了这种方法的两个严重缺陷。第一个是性能方面的问题。例如powersaved每隔五秒钟进行一次系统负载情况的采样分析的话,我们可以分析一下在下面给出的应用场景中的用户体验。假设powersaved的采样分析刚刚结束,而且由于在刚刚结束的采样周期内系统负载很低,CPU被设置在最低频率上运行。这时用户如果打开Firefox®等对CPU运算能力要求相当高的程序的话,powersaved要在下一个采样点——大约五秒钟之后才有机会观察到这种提高CPU运行频率的需求。也就是说,在Firefox启动之初的五秒钟内CPU的计算能力并没有被充分发挥出来,这无疑会使用户体验大打折扣。第二个是系统负载情况的采样分析的准确性问题。将监控系统负载情况并对未来CPU的性能需求做出判断的任务交给一个用户态程序完成实际上并不合理,一方面是由于一个用户态程序很难完整的收集到所有需要的信息,因为这些信息大部分都保存在内核空间;另一方面一个用户态程序如果想要收集这些系统信息,必然需要进行用户态与内核态之间的数据交互,而频繁的用户态与内核态之间的数据交互又会给系统性能带来负面影响。
那么这两个问题有没有解决的方法呢?应该讲社区中的开发人员就第二个问题比较容易达成一致,既然在用户态对系统的负载情况进行采集和分析存在这样那样的问题,那么更加合理的做法就是应该将这部分工作交由内核负责。但是第一个问题呢?第一个问题最直观的解决方案就是降低对系统负载进行采样分析的时间间隔,这样powersaved就能尽早的对系统负载的变化做出及时的响应。然而这种简单的降低采样分析的时间间隔的方案同样存在着两方面的问题,一方面这意味着更加频繁的用户态与内核态之间的数据交互,因此必然也就意味着对系统性能带来更大的负面影响;另一方面的主要原因在于当时各个CPU生产厂家的变频技术在硬件上仍不完善,具体体现就是在对CPU进行变频设置时所需的操作时间过长,例如Intel早期的Speedstep技术在对CPU进行变频设置时需要耗时250微秒,在此过程中CPU无法正常执行指令。读者如果简单的计算一下不难发现,即使对于一个主频为1GHz的CPU而言,250微秒也意味着250,000个时钟周期,在这期间CPU完全可以执行完上万条指令。因此从这个角度而言,简单的降低采样分析的时间间隔对系统性能带来的负面影响更加严重。幸运的是随着硬件技术的不断完善和改进,对CPU进行变频设置所需的操作时间已经显著降低,例如Intel最新的EnhancedSpeedstep技术在对CPU进行变频设置时耗时已降至10微秒,下降了不止一个数量级。正是这种CPU硬件技术的发展为内核开发人员解决这些早期的遗留问题提供了契机,Venkatesh等人提出并设计实现了一个新的名为ondemand的governor,它正是人们长期以来希望看到的一个完全在内核态下工作并且能够以更加细粒度的时间间隔对系统负载情况进行采样分析的governor。在介绍ondemandgovernor的具体实现之前,我们先来看一下如何使用ondemandgovernor及其向用户提供了哪些操作接口。通过cpufreq-set将ondemand设置为当前所使用的governor之后,在/sys/devices/system/cpu/cpuX/cpufreq目录下会出现一个名为ondemand的子目录
$sudo cpufreq-set-gondemand
$ls /sys/devices/system/cpu/cpu0/cpufreq/ondemand/
ignore_nice_load
powersave_bias
sampling_rate
sampling_rate_max
sampling_rate_min
up_threshold
$sudo cat sampling_rate_min sampling_rate sampling_rate_max
40000
80000
40000000
$sudo cat up_threshold
30
在这个子目录下名字以sampling打头的三个文件分别给出了ondemandgovernor允许使用的最短采样间隔,当前使用的采样间隔以及允许使用的最长采样间隔,三者均以微秒为单位。以笔者的电脑为例,ondemandgovernor每隔80毫秒进行一次采样。另外比较重要的一个文件是up_threshold,它表明了系统负载超过什么百分比时ondemandgovernor会自动提高CPU的运行频率。以笔者的电脑为例,这个数值为30%。那么这个表明系统负载的百分比数值是如何得到的呢?在支持Intel最新的EnhancedSpeedstep技术的CPU中,在处理器硬件中直接提供了两个MSR寄存器(ModelSpecificRegister)供ondemandgovernor采样分析系统负载情况使用。这两个MSR寄存器的名字分别为IA32_MPERF和IA32_APERF[5],其中IA32_MPERFMSR中的MPERF代表MaximumPerformance,IA32_APERFMSR中的APERF代表ActualPerformance。就像这两个MSR的名字一样,IA32_MPERFMSR寄存器是一个当CPU处在ACPIC0状态下时按照CPU硬件支持的最高运行频率每隔一个时钟周期加一的计数器;IA32_APERFMSR寄存器是一个当CPU处在ACPIC0状态下时按照CPU硬件当前的实际运行频率每隔一个时钟周期加一的计数器。有了这两个寄存器的存在,再考虑上CPU处于ACPIC0和处于ACPIC1、C2、C3三种状态下的时间比例,也就是CPU处于工作状态和休眠状态的时间比例,ondemandgovernor就可以准确的计算出CPU的负载情况了。
得到了CPU的负载情况,接下来的问题就是如何选择CPU合适的运行频率了。刚刚在前面提到,当系统负载超过up_threshold所设定的百分比时,ondemandgovernor将会自动提高CPU的运行频率,但是具体提高到哪个频率上运行呢?在ondemandgovernor监测到系统负载超过up_threshold所设定的百分比时,说明用户当前需要CPU提供更强大的处理能力,因此ondemandgovernor会将CPU设置在最高频率上运行,这一点社区中的开发人员和广大用户都没有任何异议。但是当ondemandgovernor监测到系统负载下降,可以降低CPU的运行频率时,到底应该降低到哪个频率呢?ondemandgovernor的最初实现是在可选的频率范围内调低至下一个可用频率,例如笔者使用的CPU支持三个可选频率,分别为1.67GHz、1.33GHz和1GHz,如果CPU运行在1.67GHz时ondemandgovernor发现可以降低运行频率,那么1.33GHz将被选作降频的目标频率。这种降频策略的主导思想是尽量减小对系统性能的负面影响,从而不会使得系统性能在短时间内迅速降低以影响用户体验。但是在ondemandgovernor的这种最初实现版本在社区发布后,大量用户的使用结果表明这种担心实际上是多余的,ondemandgovernor在降频时对于目标频率的选择完全可以更加激进。因此最新的ondemandgovernor在降频时会在所有可选频率中一次性选择出可以保证CPU工作在80%以上负荷的频率,当然如果没有任何一个可选频率满足要求的话则会选择CPU支持的最低运行频率。大量用户的测试结果表明这种新的算法可以在不影响系统性能的前提下做到更高效的节能。在算法改进后,ondemandgovernor的名字并没有改变,而ondemandgovernor最初的实现也保存了下来,并且由于其算法的保守性而得名conservative。
支持IntelEnhancedSpeedstep技术的CPU驱动程序的实现
前文在讨论cpufreq的软件结构时已经指出,cpufreq从设计上将CPU变频的policy与mechanism分离开来并由上层的governor负责决定CPU合适的工作频率。但是在governor根据系统负载的变化决定调整CPU的运行频率时,最终还是需要底层与CPU相关的特定驱动程序完成设置CPU运行频率的任务。这里向读者介绍一下支持Intel最新的EnhancedSpeedstep技术的CPU驱动程序的实现原理,关注的重点是如何对CPU进行变频设置。实际上支持IntelEnhancedSpeedstep技术的处理器为用户提供了非常简单的编程接口,对CPU运行频率进行设置是通过一个名为IA32_PERF_CTL的MSR寄存器进行的,另外还有一个名为IA32_PERF_STATUS的MSR寄存器可供检查CPU当前所处的运行频率。当用户需要对CPU运行频率进行设置时只需按照Intel开发手册的说明向IA32_PERF_CTLMSR寄存器中写入规定的数值即可。
总结及未来的发展方向
本文为读者介绍了变频技术在CPU硬件上的出现以及Linux内核中最初的实现存在的各种问题,并由此导致了cpufreq这一新的内核子系统的诞生。虽然早期的cpufreq模块所提供的三种governors能够在一定程度下满足用户的需要并且提供了一定的灵活性,但是由于受到当时CPU硬件技术水平的限制,仍然有很多不尽如人意的地方。之后随着CPU变频硬件技术的不断发展,尤其是IntelEnhancedSpeedstep技术的出现,原有的技术障碍被打破,随之而来的是cpufreq内核子系统有了一个全新的更加完善而高效的ondemandgovernor。
由此不难看出,内核中的cpufreq子系统是由于CPU硬件变频技术的出现而出现,同时也在随着CPU硬件变频技术的发展而发展。这其实也预示着内核中cpufreq子系统未来的发展方向,即继续跟随CPU硬件变频技术的发展脚步与时俱进。在本文的最后简单为读者介绍一下在Intel最新的CPU中针对硬件变频支持的一项新技术。前文提到在支持Intel最新的EnhancedSpeedstep技术的CPU中提供了名字分别为IA32_MPERF和IA32_APERF的两个MSR寄存器,以便为cpufreq模块所使用的governor动态收集系统的负载情况提供直接的硬件支持。其中IA32_APERFMSR寄存器当CPU处在ACPIC0状态下时按照CPU硬件当前的实际运行频率每隔一个时钟周期加一。Intel最新的处理器中进一步考虑了CPU在运行过程中由于访问内存或IO等原因可能会出现流水线停摆的状况时,IA32_APERF以前这种简单的按照CPU当前实际运行频率每隔一个时钟周期加一的做法并不能完全准确的反映CPU的负载情况。在Intel最新的处理器中如果出现流水线停摆的情况,IA32_APERF将暂时停止累加,而是在对用户真正“有用”的时间周期才会递增,这样CPU硬件就可以为cpufreq模块所使用的governor提供比以前更加准确的系统负载统计信息。
管理策略:
Linux 内部共有五种对频率的管理策略 userspace , conservative , ondemand , powersave 和 performance
? 1.performance : CPU 会固定工作在其支持的最高运行频率上;
? 2.powersave : CPU 会固定工作在其支持的最低运行频率上。因此这两种 governors 都属于静态 governor ,即在使用它们时 CPU 的运行频率不会根据系统运行时负载的变化动态作出调整。这两种 governors 对应的是两种极端的应用场景,使用 performance governor 体现的是对系统高性能的最大追求,而使用 powersave governor 则是对系统低功耗的最大追求。
? 3.Userspace :最早的 cpufreq 子系统通过 userspace governor 为用户提供了这种灵活性。系统将变频策略的决策权交给了用户态应用程序,并提供了相应的接口供用户态应用程序调节 CPU 运行频率使用。 (可以使用 Dominik 等人开发了 cpufrequtils 工具包 )
4.ondemand : userspace 是内核态的检测,效率低。而 ondemand 正是人们长期以来希望看到的一个完全在内核态下工作并且能够以更加细粒度的时间间隔对系统负载情况进行采样分析的 governor 。
? 5.conservative : ondemand governor 的最初实现是在可选的频率范围内调低至下一个可用频率。这种降频策略的主导思想是尽量减小对系统性能的负面影响,从而不会使得系统性能在短时间内迅速降低以影响用户体验。但是在 ondemand governor 的这种最初实现版本在社区发布后,大量用户的使用结果表明这种担心实际上是多余的, ondemand governor 在降频时对于目标频率的选择完全可以更加激进。因此最新的 ondemand governor 在降频时会在所有可选频率中一次性选择出可以保证 CPU 工作在 80% 以上负荷的频率,当然如果没有任何一个可选频率满足要求的话则会选择 CPU 支持的最低运行频率。大量用户的测试结果表明这种新的算法可以在不影响系统性能的前提下做到更高效的节能。在算法改进后, ondemand governor 的名字并没有改变,而 ondemand governor 最初的实现也保存了下来,并且由于其算法的保守性而得名 conservative 。
Ondemand 降频更加激进, conservative 降频比较缓慢保守,事实使用 ondemand 的效果也是比较好的。
使用方法:
? cd sys/devices/system/cpu/cpu0/cpufreq/ 目录
echo 32000 > scaling_min_freq 设置最小工作频率 (khz,32000~88000)
// 若想使用 userspace 策略
# echo userspace > scaling_governor 切换工作方式为 userspace
echo 64000 > scaling_setspeed 设置成想要的工作频率 ( khz )
// 若想使用 ondemand 策略
# echo ondemand > scaling_governor 切换工作方式为 ondemand
3. 如何实现?
首先需要干一些杂活,修改 kconfig makefile 把系统屏蔽的 cpufreq 打开,对于我们来说主要的核心有两部分:
系统相关:主要有 cpu , timer (变了频率一定要更新系统 timer ,否则系统时间就不准了), sdram 等。
主要就是实现下面这个结构体:
static struct cpufreq_driver sep4020_driver =
{
.flags = CPUFREQ_STICKY,
.verify = sep4020_verify_speed,
.target = sep4020_target,
.get = sep4020_getspeed,
.init = sep4020_cpu_init,
.name = "SEP4020 Freq",
};
代码还是很简陋,很多细节都没考虑,所以具体的暂时先不讲了,大家可以先参考 pxa 和 sa1100 的实现。
然后就是收频率影响的驱动:
简单的来说就是:系统在变化 cpu 主频的时候会调用 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); 函数,响挂载在这个 cpu 上所有的驱动发出一个信号,驱动接收到这个信号则调用相应的处理函数。
这里把串口部分的实现简化,如下:
#ifdef CONFIG_CPU_FREQ
static int sep4020_serial_cpufreq_transition(struct notifier_block *nb, unsigned long val, void *data)
{
// printk("in the serial cpufreq_transition\n");
int pmcr_pre;
unsigned long cpu_clk,baud,baudh,baudl;
pmcr_pre = *(volatile unsigned long*)PMU_PMCR_V;
if(pmcr_pre > 0x4000)
cpu_clk = (pmcr_pre-0x4000)*8000000;
else
cpu_clk = (pmcr_pre)*4000000;
baud = cpu_clk/16/115200;
baudh = baud >>8;
baudl = baud&0xff;
*(volatile unsigned char*)UART0_LCR_V |= (0x80);
*(volatile unsigned char*)UART0_DLBL_V = baudl;
*(volatile unsigned char*)UART0_DLBH_V = baudh;
*(volatile unsigned char*)UART0_LCR_V &= ~(0x80);
printk("in the serial cpufreq_transition\n");
return 0;
}
static inline int sep4020_serial_cpufreq_register(void)
{
sep4020_serial_freq_transition.notifier_call = sep4020_serial_cpufreq_transition;
return cpufreq_register_notifier(&sep4020_serial_freq_transition,
CPUFREQ_TRANSITION_NOTIFIER);
}
static inline void sep4020_serial_cpufreq_deregister(void)
{
cpufreq_unregister_notifier(&sep4020_serial_freq_transition,
CPUFREQ_TRANSITION_NOTIFIER);
}
#else
#endif
在这个子目录下名字以 sampling 打头的三个文件分别给出了 ondemand governor 允许使用的最短采样间隔,当前使用的采样间隔以及允许使用的最长采样间隔,三者均以微秒为单位。以笔者的电脑为例, ondemand governor 每隔 80 毫秒进行一次采样。另外比较重要的一个文件是 up_threshold ,它表明了系统负载超过什么百分比时 ondemand governor 会自动提高 CPU 的运行频率。以笔者的电脑为例,这个数值为 30% 。那么这个表明系统负载的百分比数值是如何得到的呢?在支持 Intel 最新的 Enhanced Speedstep 技术的 CPU 中,在处理器硬件中直接提供了两个 MSR 寄存器(Model Specific Register)供 ondemand governor 采样分析系统负载情况使用。这两个 MSR 寄存器的 名字分别为 IA32_MPERF 和 IA32_APERF[5] ,其中 IA32_MPERF MSR 中的 MPERF 代表 Maximum Performance , IA32_APERF MSR 中的 APERF 代表 Actual Performance 。就像这两个 MSR 的名字一样, IA32_MPERF MSR 寄存器是一个当 CPU 处在 ACPI C0 状态下时按照 CPU 硬件支持的最高运行频率每隔一个时钟周期加一的计数器; IA32_APERF MSR 寄存器是一个当 CPU 处在 ACPI C0 状态下时按照 CPU 硬件当前的实际运行频率每隔一个时钟周期加一的计数器。有了这两个寄存器的存在,再考虑上 CPU 处于 ACPI C0 和处于 ACPI C1、C2、C3 三种状态下的时间比例,也就是 CPU 处于工作状态和休眠状态的时间比例, ondemand governor 就可以准确的计算出 CPU 的负载情况了。
得到了 CPU 的负载情况,接下来的问题就是如何选择 CPU 合适的运行频率了。刚刚在前面提到,当系统负载超过 up_threshold 所设定的百分比时, ondemand governor 将会自动提高 CPU 的运行频率,但是具体提高到哪个频率上运行呢?在 ondemand governor 监测到系统负载超过 up_threshold 所设定的百分比时,说明用户当前需要 CPU 提供更强大的处理能力,因此 ondemand governor 会将CPU设置在最高频率上运行,这一点社区中的开发人员和广大用户都没有任何异议。但是当 ondemand governor 监测到系统负载下降,可以降低 CPU 的运行频率时,到底应该降低到哪个频率呢? ondemand governor 的最初实现是在可选的频率范围内调低至下一个可用频率,例如笔者使用的 CPU 支持三个可选频率,分别为 1.67GHz、 1.33GHz 和 1GHz ,如果 CPU 运行在 1.67GHz 时 ondemand governor 发现可以降低运行频率,那么 1.33GHz 将被选作降频的目标频率。这种降频策略的主导思想是尽量减小对系统性能的负面影响,从而不会使得系统性能在短时间内迅速降低以影响用户体验。但是在 ondemand governor 的这种最初实现版本在社区发布后,大量用户的使用结果表明这种担心实际上是多余的, ondemand governor 在降频时对于目标频率的选择完全可以更加激进。因此最新的 ondemand governor 在降频时会在所有可选频率中一次性选择出可以保证 CPU 工作在 80% 以上负荷的频率,当然如果没有任何一个可选频率满足要求的话则会选择 CPU 支持的最低运行频率。大量用户的测试结果表明这种新的算法可以在不影响系统性能的前提下做到更高效的节能。在算法改进后, ondemand governor 的名字并没有改变,而 ondemand governor 最初的实现也保存了下来,并且由于其算法的保守性而得名 conservative 。