4、Governor详解(中):Ondemand Governor原理、Conservative Governor原理、Schedutil Governor原理
好,咱们接着聊。上一章我把Performance和Powersave这两个“极端派”讲透了。这一章,咱们来啃三块硬骨头:Ondemand、Conservative和Schedutil。这三个家伙,才是日常工作中打交道最多的调频策略。
我个人习惯把Governor分成两类:一类是“反应型”,比如Ondemand和Conservative,它们看CPU负载变化再动;另一类是“预测型”,比如Schedutil,它直接看调度器的“内心戏”。理解了这个区别,后面的内容就好办了。
4.1 Ondemand Governor:老牌劲旅,简单粗暴
Ondemand,名字就说明了它的态度——“按需”。它是最早进入Linux内核的动态调频策略之一,至今仍在大量嵌入式设备上服役。
核心原理:
Ondemand会周期性地(通常每10ms或20ms)采样CPU的负载。它定义了两个关键阈值:up_threshold和sampling_down_factor。
- 升频条件:当采样到的CPU负载超过
up_threshold(默认80%),立即将频率跳到最高档。 - 降频逻辑:负载低于阈值时,它会逐步降低频率,但降频速度远慢于升频。这是为了防止频繁震荡。
关键参数一览:
| 参数名 | 默认值 | 说明 |
|---|---|---|
| up_threshold | 80 | 负载超过此值,立即升频到最高 |
| sampling_down_factor | 1 | 降频时的采样周期倍数,越大降频越慢 |
| ignore_nice_load | 0 | 是否忽略nice值为负的进程负载 |
我在项目中遇到过一个问题:某款平板电脑,播放视频时CPU频率频繁跳变,导致画面卡顿。查了半天,发现是Ondemand的sampling_down_factor设置得太小,降频太快。视频解码线程刚释放CPU,负载一降,频率就掉下去了;下一个帧一来,负载又冲上去,频率再跳回来。嗯,这就是典型的“乒乓效应”。
避坑指南:我曾经把up_threshold调低到60%,想提高响应速度。结果呢?CPU动不动就飙到最高频,功耗直接炸了。后来我学乖了:Ondemand的阈值,不是越低越好。80%是经过大量测试的平衡点,除非你很清楚自己在做什么,否则别乱动。
4.2 Conservative Governor:温和派,步步为营
Conservative,翻译过来就是“保守”。它和Ondemand是亲戚,但性格完全不同。Ondemand是“一有风吹草动就全力冲刺”,Conservative则是“先试探一下,不行再加大力度”。
核心区别:
- Ondemand升频:一步到位,直接跳到最高。
- Conservative升频:每次只跳一档(比如从1.0GHz升到1.2GHz),逐步试探。
它引入了两个额外的阈值:down_threshold和freq_step。
- 升频条件:负载超过
up_threshold(默认80%),但只增加freq_step(默认5%)的频率。 - 降频条件:负载低于
down_threshold(默认20%),才降低一档频率。
你想想看,这种策略的好处是什么?说白了,就是平滑。对于负载波动不大的场景(比如后台下载、音乐播放),Conservative能保持频率稳定,避免不必要的功耗尖峰。
但代价也很明显:响应慢。如果负载突然从10%飙升到90%,Conservative需要多次采样才能升到最高频。我曾在某款路由器上测试过,Web服务突然收到大量请求时,Conservative的响应延迟比Ondemand高了将近30ms。对于实时性要求高的场景,这可能是致命的。
注意:Conservative的down_threshold默认是20%,这意味着CPU负载要降到20%以下才会降频。如果你发现设备在轻负载下频率依然很高,检查一下这个值是不是设得太低了。
4.3 Schedutil Governor:调度器的心腹
终于讲到Schedutil了。这是目前Linux内核中最先进的调频策略,也是我个人的首选。为什么?因为它不再依赖“采样”这种事后诸葛亮的方式,而是直接从调度器获取CPU的“真实需求”。
原理简述:
Schedutil是调度器(CFS)的一部分。它利用调度器内部的PELT(Per-Entity Load Tracking)机制,实时跟踪每个任务的负载。当调度器决定把一个任务放到某个CPU上运行时,Schedutil就已经知道这个任务需要多少算力了。
用大白话说:Ondemand是“看后视镜开车”,Schedutil是“看导航开车”。
核心公式:
next_freq = max_freq * (util / max_capacity)
其中util是当前CPU的利用率(来自PELT),max_capacity是CPU的最大算力。这个公式简单到令人发指,但效果出奇的好。
我在项目中遇到过最典型的案例:某款手机在运行《王者荣耀》时,使用Ondemand会出现“掉帧-升频-降帧”的循环。换成Schedutil后,帧率曲线平滑得像一条直线。为什么?因为Schedutil能提前感知到游戏线程的算力需求,在掉帧发生之前就把频率提上去了。
Schedutil的三大优势:
- 响应快:无需采样周期,调度器决策的同时就决定了频率。
- 更精准:基于任务级别的负载,而不是CPU级别的负载。
- 功耗低:避免了Ondemand那种“过度升频”的问题。
但Schedutil也不是万能的。它依赖调度器的PELT机制,而PELT本身有一个“衰减窗口”的概念。如果任务运行时间极短(比如微秒级的中断处理),PELT可能还没来得及更新负载,任务就结束了。这时候Schedutil的频率决策就会滞后。
调优建议:如果你在使用Schedutil时发现某些场景下响应不够快,可以尝试调整schedutil_rate_limit_us参数。这个参数控制Schedutil的最小更新间隔,默认是1000微秒。把它调小到500微秒,能提升响应速度,但会增加一点CPU开销。我曾经在服务器上做过测试,从1000降到500,响应延迟降低了15%,CPU开销只增加了不到1%。
4.4 三个Governor的对比总结
| 特性 | Ondemand | Conservative | Schedutil |
|---|---|---|---|
| 升频策略 | 一步到位 | 逐步试探 | 按需计算 |
| 降频策略 | 较慢 | 更慢 | 实时调整 |
| 响应速度 | 快 | 慢 | 最快 |
| 功耗控制 | 一般 | 较好 | 优秀 |
| 适用场景 | 交互式设备 | 后台任务 | 通用场景 |
最后说一句:没有最好的Governor,只有最适合的。如果你在做手机或平板,我建议优先考虑Schedutil;如果是嵌入式设备,Ondemand依然是个稳妥的选择;至于Conservative,嗯,除非你对功耗有极致要求,否则我一般不太推荐。
下一章,咱们聊聊如何在实际项目中选择和调优这些Governor。到时候我会分享一些具体的调试工具和实战案例,敬请期待。