4. CPU频率与电压调节:cpufreq governor原理与配置
CPU频率和电压的调节,说白了就是让芯片在「够用」和「省电」之间找到平衡点。我做了这么多年嵌入式,见过太多因为 governor 选错导致功耗翻车的案例。今天咱们就把 ondemand、conservative、schedutil 这三个主力 governor 掰开揉碎讲清楚。
4.1 cpufreq 框架概览
先看整体架构。cpufreq 是 Linux 内核中负责 CPU 频率/电压动态调节的核心框架。它分三层:
- 核心层 (cpufreq core):提供统一的 sysfs 接口,管理 governor 的注册与切换
- governor 层:决定「什么时候调、调到多少」的策略模块
- 驱动层 (cpufreq driver):直接操作硬件寄存器,完成频率/电压的物理切换
用户空间通过 /sys/devices/system/cpu/cpuX/cpufreq/ 下的文件与内核交互。我个人习惯先用 cpufreq-info 看一眼当前硬件支持的频率范围,再决定用哪个 governor。
核心概念:P-state(性能状态)是频率-电压组合对。每个 P-state 对应一组 {频率, 电压}。governor 的工作就是在可用 P-state 列表中做选择。
4.2 ondemand governor:经典激进派
ondemand 是 Linux 社区最早广泛使用的动态 governor。它的核心逻辑很简单:CPU 负载超过阈值就立刻跳到最高频,负载降下来再逐步回落。
工作原理:
- 每隔
sampling_rate(默认 10ms)采样一次 CPU 负载 - 负载 >
up_threshold(默认 95%),直接跳到最高可用频率 - 负载回落时,按
sampling_down_factor步进降频
我的经验:ondemand 在交互式场景(比如 Android 早期版本)表现不错,因为「一有活就全力跑」的响应很快。但注意,频繁的「跳频-降频」会产生额外的电压切换功耗。我在一个平板项目里发现,ondemand 导致 1 秒内跳频 20 多次,白白浪费了切换开销。
关键 sysfs 参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
sampling_rate | 10000 (μs) | 采样间隔,越小响应越快,但 CPU 开销越大 |
up_threshold | 95 | 升频阈值(百分比),超过此值直接跳最高频 |
sampling_down_factor | 1 | 降频步进系数,越大降频越慢 |
ignore_nice_load | 0 | 是否忽略 nice 值较高的进程负载 |
注意:ondemand 的升频策略是「一步到位」,这在负载波动大的场景下会导致频率剧烈抖动。我曾经在一个网络转发设备上看到,ondemand 让 CPU 在 200MHz 和 1.2GHz 之间来回弹,功耗反而比固定高频还高。后来我换成了 conservative。
4.3 conservative governor:温和渐进派
conservative 和 ondemand 的采样机制类似,但调频策略完全不同。它不会「一步跳最高」,而是按步进值逐步升频。
核心差异:
- 升频:每次只增加
freq_step(默认 5%)的幅度 - 降频:同样按步进逐步降低
- 多了
down_threshold参数,负载低于此值才开始降频
我举个例子你就明白了。假设 CPU 有 5 个频率档位:200、400、600、800、1000 MHz。当前在 200 MHz,负载突然到 100%:
- ondemand:直接跳到 1000 MHz
- conservative:200 → 400 → 600 → 800 → 1000,每次采样周期升一档
conservative 的升频延迟更大,但频率变化更平滑。适合后台任务、持续负载型场景,比如视频编码、文件压缩。
| 对比项 | ondemand | conservative |
|---|---|---|
| 升频策略 | 一步到位(跳最高) | 步进升频(默认 5%) |
| 响应速度 | 快(1 个采样周期) | 慢(N 个采样周期) |
| 频率抖动 | 大 | 小 |
| 适用场景 | 交互式、突发负载 | 持续负载、后台任务 |
| 切换开销 | 高(频繁大幅跳频) | 低(小幅步进) |
避坑指南:我曾经在一个 IoT 网关项目里用了 conservative,结果发现 CPU 温度波动很大。原因是升频太慢,负载上来时频率跟不上,导致 CPU 在低频高负载下运行,效率反而更低。后来我把 freq_step 从 5% 调到 15%,问题就解决了。所以 conservative 的参数一定要根据实际负载特征调整。
4.4 schedutil governor:调度器原生派
schedutil 是 Linux 4.7 引入的「调度器感知」governor。它不依赖独立的采样定时器,而是直接利用 CFS 调度器(完全公平调度器)的负载跟踪数据来做调频决策。
为什么会有 schedutil?
- ondemand/conservative 的采样周期是固定的,无法感知微秒级的负载变化
- 调度器本身就在跟踪每个 CPU 的 util(利用率),这个数据更实时、更精确
- schedutil 把调频和调度「对齐」了,减少了不必要的频率切换
schedutil 的核心公式(简化版):
next_freq = current_freq * (util / capacity)
// util:调度器跟踪的 CPU 利用率(0 ~ 1024)
// capacity:当前频率下的最大计算能力
这个公式意味着:schedutil 会尝试让频率「刚好匹配」当前负载,而不是像 ondemand 那样非高即低。
我的建议:如果你的内核版本 ≥ 4.7,且使用 CFS 调度器,优先考虑 schedutil。它在大多数场景下比 ondemand 省电 5%~15%,而且响应延迟更低。我在一个 ARM64 服务器项目里做过对比测试,schedutil 在混合负载下比 ondemand 省电 12%,同时 99% 响应延迟还降低了 8%。
schedutil 的调频粒度更细,因为它能感知到每个调度周期(通常 1~4ms)的负载变化。但要注意,它依赖调度器数据,如果系统使用了实时调度器(RT)或 Deadline 调度器,schedutil 的行为会有所不同。
4.5 实战配置指南
说了这么多理论,咱们直接上手。以下是我在实际项目中总结的配置策略:
- 查看当前 governor:
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor - 切换 governor:
echo "schedutil" > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor - 调整 ondemand 参数(示例):
echo 5000 > /sys/devices/system/cpu/cpufreq/ondemand/sampling_rate echo 80 > /sys/devices/system/cpu/cpufreq/ondemand/up_threshold - 调整 conservative 参数(示例):
echo 10 > /sys/devices/system/cpu/cpufreq/conservative/freq_step echo 60 > /sys/devices/system/cpu/cpufreq/conservative/down_threshold - 永久生效(嵌入式设备):在 init 脚本或 systemd service 中写入上述命令
重要提醒:不是所有硬件平台都支持所有 governor。比如一些老旧 ARM 芯片的 cpufreq 驱动只实现了 cpufreq_driver.target 回调,不支持 schedutil 需要的 target_index 接口。切换前先用 cpufreq-info -g 查看可用 governor 列表。
4.6 选型决策树
最后,我根据多年经验总结了一个简单的选型思路:
- 交互式 UI 设备(手机、平板、触控屏):schedutil > ondemand > conservative
- 后台持续任务(视频编码、日志采集):conservative > schedutil > ondemand
- 实时性要求高(工业控制、音频处理):performance(固定高频)或 schedutil + 调高升频阈值
- 电池供电 IoT(低功耗传感器):powersave(固定低频)或 conservative + 低 down_threshold
嗯,governor 的选择没有银弹。我建议你在项目初期就把几种 governor 都跑一遍,用功耗仪或 perf stat 采集实际数据,而不是凭感觉选。毕竟,理论再漂亮,不如实测数据有说服力。