内核中的 cpufreq 子系统通过 sysfs 文件系统向上层应用提供了用户接口,对于系统中的每一个 CPU 而言,其 cpufreq 的 sysfs 用户接口位于 /sys/devices/system/cpu/cpuX/cpufreq/ 目录下,其中 X 代表 processor id ,与 /proc/cpuinfo 中的信息相对应。以cpu0 为例,用户一般会在该目录下观察到以下文件: <br /><br /><br /><br />$ ls -F /sys/devices/system/cpu/cpu0/cpufreq/ <br />affected_cpus <br />cpuinfo_cur_freq <br />cpuinfo_max_freq <br /><br /><br /><br /> cpuinfo_min_freq <br />ondemand/ <br />scaling_available_frequencies <br />scaling_available_governors <br />scaling_cur_freq <br />scaling_driver <br />scaling_governor <br />scaling_max_freq <br />scaling_min_freq <br />stats/ <br /> <br /><br /><br /><br /> 这其中的所有可读文件都可以使用 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 目录则与 ondemand governor 相关,在后文会进行相应的介绍。 <br /><br /><br /><br /> 通过以上的介绍,大家对如何使用 cpufreq 通过 sysfs 提供的用户接口已经有了大致的了解,但是对于绝大部分用户而言,逐一操作这些文件既费力又耗时。因此 Dominik 等人开发了cpufrequtils 工具包[2],为用户提供了更加简便的对内核cpufreq 子系统的操作接口。通过 cpufreq-info 的输出,读者可以很清楚的看到刚刚在上面介绍过的/sys/devices/system/cpu/cpuX/cpufreq/ 目录下各个文件的内容。 <br /><br /><br /><br />$ cpufreq-info <br />cpufrequtils 002: cpufreq-info (C) Dominik Brodowski <br /><br /><br /><br />2004-2006 <br />Report errors and bugs to linux@brodo.de, please. <br />analyzing CPU 0: <br /> driver: acpi-cpufreq <br /> CPUs which need to switch frequency at the same time: <br /><br /><br /><br />0 1 <br /> hardware limits: 1000 MHz - 1.67 GHz <br /> available frequency steps: 1.67 GHz, 1.33 GHz, 1000 <br /><br /><br /><br />MHz <br /> available cpufreq governors: userspace, conservative, <br /><br /><br /><br />ondemand, powersave, performance <br /> current policy: frequency should be within 1000 MHz <br /><br /><br /><br />and 1.67 GHz. <br /> The governor "ondemand" may decide which <br /><br /><br /><br />speed to use <br /> within this range. <br /> current CPU frequency is 1000 MHz. <br />analyzing CPU 1: <br /> driver: acpi-cpufreq <br /> CPUs which need to switch frequency at the same time: <br /><br /><br /><br />0 1 <br /> hardware limits: 1000 MHz - 1.67 GHz <br /> available frequency steps: 1.67 GHz, 1.33 GHz, 1000 <br /><br /><br /><br />MHz <br /> available cpufreq governors: userspace, conservative, <br /><br /><br /><br />ondemand, powersave, performance <br /> current policy: frequency should be within 1000 MHz <br /><br /><br /><br />and 1.67 GHz. <br /> The governor "ondemand" may decide which <br /><br /><br /><br />speed to use <br /> within this range. <br /> current CPU frequency is 1000 MHz. <br /><br /><br /><br /><br /> <br /><br /><br /><br /> Ondemand governor 的由来及其实现刚刚我们在 cpufreq-info 的输出中可以看到 cpufreq 子系统一共提供了五种 governors 供用户选择使用,它们分别是 userspace,conservative,ondemand,powersave 和performance。在最新的内核中如果用户不进行额外设置的话,ondemand 会被作为默认的 governor 使用。为了理解是什么原因造成了这种现状,我们在这里带领读者回顾一下 cpufreq 子系统中的governor在内核中的开发历史。 <br /><br /><br /><br /> Cpufreq 作为一个子系统最早被加入到 Linux 内核中时只配备了三个governors ,分别是performance、powersave 和userspace。当用户选择使用 performance governor 时,CPU会固定工作在其支持的最高运行频率上;当用户选择使用powersave governor 时,CPU会固定工作在其支持的最低运行频率上。因此这两种 governors 都属于静态 governor ,即在使用它们时 CPU 的运行频率不会根据系统运行时负载的变化动态作出调整。这两种 governors 对应的是两种极端的应用场景,使用 performance governor 体现的是对系统高性能的最大追求,而使用 powersave governor 则是对系统低功耗的最大追求。虽然这两种应用需求确实存在,但大多数用户在大部分时间里需要的是更加灵活的变频策略。最早的 cpufreq 子系统通过 userspace governor 为用户提供了这种灵活性。正如它的名字一样,使用 userspace governor 时,系统将变频策略的决策权交给了用户态应用程序,并提供了相应的接口供用户态应用程序调节 CPU 运行频率使用。通过使用 cpufrequtils 工具包中的 cpufreq-set 将 userspace 设置为 cpufreq 子系统所使用的 governor 后,我们可以看到与之前相比在 /sys/devices/system/cpu/cpuX/cpufreq/ 目录下多出了一个名为 scaling_setspeed 的文件,这正是 userspace governor 所提供的特殊用户接口。用户可以通过向该文件写入任何一个 scaling_available_frequencies 中所支持的运行频率,从而将 CPU 设置在该频率下运行。 <br /><br /><br /><br /># cpufreq-set -g userspace <br /># cat cpuinfo_cur_freq <br />1000000 <br /># cat scaling_available_frequencies <br />1667000 1333000 1000000 <br /># echo 1333000 >scaling_setspeed <br /># cat cpuinfo_cur_freq <br />1333000 <br /> <br /><br /><br /><br /> 刚刚提到在使用 userspace governor 时,系统将变频策略的决策权交给了用户态应用程序。该用户态应用程序一般是一个 daemon 程序,每隔一定的时间间隔收集一次系统信息并根据系统的负载情况使用 userspace governor 提供的 scaling_setspeed 接口动态调整 CPU 的运行频率。作为这个daemon 程序,当时在几个主要的 Linux 发行版中使用的一般是 powersaved 或者 cpuspeed。这两个 daemon 程序一般每隔几秒钟统计一次 CPU 在这个采样周期内的负载情况,并根据统计结果调整 CPU 的运行频率。这种 userspace governor 加用户态 daemon 程序的变频方法虽然为用户提供了一定的灵活性,但通过开源社区的广泛使用所得到的意见反馈逐渐暴露了这种方法的两个严重缺陷。第一个是性能方面的问题。例如powersaved 每隔五秒钟进行一次系统负载情况的采样分析的话,我们可以分析一下在下面给出的应用场景中的用户体验。假设 powersaved 的采样分析刚刚结束,而且由于在刚刚结束的采样周期内系统负载很低,CPU 被设置在最低频率上运行。这时用户如果打开 Firefox® 等对 CPU 运算能力要求相当高的程序的话,powersaved 要在下一个采样点——大约五秒钟之后才有机会观察到这种提高 CPU 运行频率的需求。也就是说,在Firefox 启动之初的五秒钟内 CPU 的计算能力并没有被充分发挥出来,这无疑会使用户体验大打折扣。第二个是系统负载情况的采样分析的准确性问题。将监控系统负载情况并对未来 CPU 的性能需求做出判断的任务交给一个用户态程序完成实际上并不合理,一方面是由于一个用户态程序很难完整的收集到所有需要的信息,因为这些信息大部分都保存在内核空间;另一方面一个用户态程序如果想要收集这些系统信息,必然需要进行用户态与内核态之间的数据交互,而频繁的用户态与内核态之间的数据交互又会给系统性能带来负面影响。 <br /><br /><br /><br /> 那么这两个问题有没有解决的方法呢?应该讲社区中的开发人员就第二个问题比较容易达成一致,既然在用户态对系统的负载情况进行采集和分析存在这样那样的问题,那么更加合理的做法就是应该将这部分工作交由内核负责。但是第一个问题呢?第一个问题最直观的解决方案就是降低对系统负载进行采样分析的时间间隔,这样 powersaved 就能尽早的对系统负载的变化做出及时的响应。然而这种简单的降低采样分析的时间间隔的方案同样存在着两方面的问题,一方面这意味着更加频繁的用户态与内核态之间的数据交互,因此必然也就意味着对系统性能带来更大的负面影响;另一方面的主要原因在于当时各个 CPU 生产厂家的变频技术在硬件上仍不完善,具体体现就是在对 CPU 进行变频设置时所需的操作时间过长,例如 Intel 早期的 Speedstep 技术在对 CPU 进行变频设置时需要耗时 250 微秒,在此过程中 CPU 无法正常执行指令。读者如果简单的计算一下不难发现,即使对于一个主频为 1GHz 的 CPU 而言, 250 微秒也意味着 250,000 个时钟周期,在这期间 CPU 完全可以执行完上万条指令。因此从这个角度而言,简单的降低采样分析的时间间隔对系统性能带来的负面影响更加严重。幸运的是随着硬件技术的不断完善和改进,对 CPU 进行变频设置所需的操作时间已经显著降低,例如 Intel 最新的 Enhanced Speedstep 技术在对 CPU 进行变频设置时耗时已降至 10 微秒,下降了不止一个数量级。正是这种 CPU 硬件技术的发展为内核开发人员解决这些早期的遗留问题提供了契机,Venkatesh 等人提出并设计实现了一个新的名为 ondemand 的 governor ,它正是人们长期以来希望看到的一个完全在内核态下工作并且能够以更加细粒度的时间间隔对系统负载情况进行采样分析的 governor。在介绍 ondemand governor 的具体实现之前,我们先来看一下如何使用 ondemand governor 及其向用户提供了哪些操作接口。通过 cpufreq-set 将 ondemand 设置为当前所使用的 governor 之后,在 /sys/devices/system/cpu/cpuX/cpufreq 目录下会出现一个名为 ondemand 的子目录<br /> |
|