4、CPU频率调节(CPUFreq):Governor策略深度解析

说到嵌入式Linux的低功耗设计,CPU频率调节这块儿,我敢说是个绕不开的坎儿。很多工程师一上来就想着怎么把主频压到最低,结果系统卡得跟幻灯片似的。其实,调频这事儿,讲究的是「该快的时候快,该省的时候省」。

我个人习惯把CPUFreq比作汽车的变速箱。你想想看,平路巡航用高档位省油,爬坡就得降档拉高转速。CPU也一样,负载轻的时候降频省电,任务重了就得提频保证响应。今天咱们就好好聊聊这背后的Governor策略。

4.1 Governor是什么?

Governor,翻译过来就是「调控器」。它是一套内核里的策略算法,决定了CPU频率该怎么变。Linux内核里默认集成了好几种,每种都有自己的脾气。

我刚开始做嵌入式项目时,总觉得这玩意儿随便选一个就行。后来有一次,产品在低温环境下频繁死机,排查了三天才发现是Governor配置不当导致的。嗯,从那以后我再也不敢小看它了。

4.2 五大Governor策略详解

目前主流的Governor有五个:performancepowersaveondemandconservativeschedutil。咱们一个一个说。

4.2.1 performance:全力输出

这个策略最简单粗暴——直接把CPU频率拉到最高,并且一直保持。说白了就是「我不管功耗,我只要性能」。

适用场景:跑分测试、实时性要求极高的场景(比如音频处理)、或者你根本不在乎电池续航。

我在项目中遇到过客户要求「必须流畅播放4K视频」,当时用的就是performance。但代价也很明显——设备发热严重,续航直接砍半。所以,除非万不得已,我不建议在产品里长期用这个。

4.2.2 powersave:极致省电

跟performance正好相反,powersave把频率固定在最低档。系统会变得很慢,但功耗确实低。

说实话,这个策略在实际产品中用得不多。因为用户可受不了「点一下等三秒」的体验。不过,在一些传感器采集节点、或者设备待机状态下,倒是可以临时切过去。

我曾经在一个电池供电的温湿度采集器上,用powersave配合深度休眠,把待机电流降到了50uA以下。但注意,唤醒后一定要切回其他策略,否则数据上报会严重延迟。

4.2.3 ondemand:按需分配

这是Linux内核的默认策略,也是很多工程师最熟悉的。它的逻辑很简单:

  • CPU负载低时,频率也低
  • 一旦负载超过某个阈值(默认95%),立刻跳到最高频
  • 负载降下来后,再慢慢降频

你想想看,这种「跳变式」的调频方式,其实有个问题——响应太快了。有时候只是瞬间的负载尖峰,它也会把频率拉满,造成不必要的功耗浪费。

我记得有一次做平板电脑项目,用户反映「看网页时电池掉得飞快」。一查,发现是ondemand的阈值设得太敏感,浏览器滚动一下页面就触发升频。后来我把阈值调到80%,并增加了降频的延迟时间,续航提升了将近15%。

4.2.4 conservative:温和派

conservative跟ondemand有点像,但调频方式更「温柔」。它不是一步跳到最高频,而是逐级升降。比如从800MHz到1.2GHz,它会先升到1.0GHz,看看负载情况,再决定要不要继续升。

这种策略的好处是功耗更平滑,不会出现「突然飙高又突然掉下来」的情况。但缺点也很明显——响应慢。如果系统突然来了个大任务,conservative可能来不及提频,导致卡顿。

保守策略不适合交互式应用。比如触控屏设备,用户点一下按钮,结果等半秒才有反应,这体验就太差了。

4.2.5 schedutil:调度器联动

这是后来加入内核的新策略,也是我个人比较推荐的一个。它跟进程调度器(scheduler)深度绑定,直接从调度器那里获取CPU利用率信息,而不是靠采样。

说白了,schedutil能「预判」负载变化。比如一个进程刚被唤醒,调度器知道它马上要跑,schedutil就能提前把频率提上去。这种前瞻性的调频方式,既保证了性能,又避免了不必要的频率波动。

我在一个ARM64的多核项目上做过对比测试:同样的工作负载,schedutil比ondemand省电约8%,而且响应延迟还更低。所以,如果你的内核版本支持(Linux 4.7+),我建议优先考虑schedutil。

4.3 用户态调频 vs 内核态调频

聊完策略,咱们再来说说调频的两种方式。

4.3.1 内核态调频

内核态调频,就是由内核里的Governor自动完成。你只需要在设备树或内核配置里选好策略,剩下的交给内核。这种方式简单、可靠,适合大多数场景。

配置方法也很直接:

# 查看当前支持的Governor
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors

# 设置Governor为schedutil
echo schedutil > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

# 查看当前频率
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq

嗯,这里要注意:有些SoC可能不支持所有Governor,具体要看硬件驱动是否实现了对应的调频接口。

4.3.2 用户态调频

用户态调频,就是由应用程序或用户脚本直接控制频率。这种方式更灵活,但也更危险——搞不好就把系统搞崩了。

用户态调频通常通过cpufreq-set工具或者直接写sysfs节点来实现:

# 设置最大频率
echo 1200000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq

# 设置最小频率
echo 600000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq

# 或者用cpufreq-set工具
cpufreq-set -c 0 -g performance
cpufreq-set -c 0 -u 1.2GHz
用户态调频的典型应用场景:游戏模式(切到performance)、省电模式(切到powersave)、或者根据温度传感器动态调整频率。

我曾经在一个无人机项目里用过用户态调频。当时飞控算法需要高频率执行,但电机驱动又怕过热。我写了个守护进程,实时读取温度传感器,温度超过65°C就强制降频,低于50°C再恢复。效果还不错,但调试过程挺折腾的——有一次写错了频率值,直接把CPU锁死了,无人机差点炸机。

用户态调频一定要做边界检查!频率值不能低于硬件支持的最低值,也不能高于最高值。否则轻则调频失败,重则系统死机。

4.4 如何选择?我的建议

说了这么多,到底该怎么选?我根据自己的经验,给个参考:

场景 推荐Governor 理由
交互式设备(手机、平板) schedutil 或 ondemand 响应快,功耗均衡
服务器/高性能计算 performance 性能优先,不在乎功耗
电池供电的传感器节点 powersave + 定时唤醒 极致省电
音视频播放 ondemand 或 schedutil 避免卡顿,同时省电
实时控制系统 performance 频率稳定,延迟可控

最后说一句:没有万能的Governor。最好的做法是在产品开发阶段,把几种策略都跑一遍,用功耗仪和性能测试工具实测数据。毕竟,纸上得来终觉浅,绝知此事要躬行嘛。

下一章,咱们聊聊CPU Idle状态和WFI指令,看看CPU没事干的时候怎么省电。敬请期待。