〇、全景:CPU 有活干时,可以"跑慢点"省电
上一篇 cpuidle 讲的是"CPU 没活干时睡多深"。但 CPU 有活干 时,也不是非得全速跑------如果负载很轻,把频率降下来、电压降下来,照样能完成任务,还省电。这就是 cpufreq(P-state,性能状态) 管的事:决定 CPU 当前跑多快。
#mermaid-svg-2cSM7MDEgy7rfm5M{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2cSM7MDEgy7rfm5M .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2cSM7MDEgy7rfm5M .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2cSM7MDEgy7rfm5M .error-icon{fill:#552222;}#mermaid-svg-2cSM7MDEgy7rfm5M .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2cSM7MDEgy7rfm5M .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2cSM7MDEgy7rfm5M .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2cSM7MDEgy7rfm5M .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2cSM7MDEgy7rfm5M .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2cSM7MDEgy7rfm5M .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2cSM7MDEgy7rfm5M .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2cSM7MDEgy7rfm5M .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2cSM7MDEgy7rfm5M .marker.cross{stroke:#333333;}#mermaid-svg-2cSM7MDEgy7rfm5M svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2cSM7MDEgy7rfm5M p{margin:0;}#mermaid-svg-2cSM7MDEgy7rfm5M .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2cSM7MDEgy7rfm5M .cluster-label text{fill:#333;}#mermaid-svg-2cSM7MDEgy7rfm5M .cluster-label span{color:#333;}#mermaid-svg-2cSM7MDEgy7rfm5M .cluster-label span p{background-color:transparent;}#mermaid-svg-2cSM7MDEgy7rfm5M .label text,#mermaid-svg-2cSM7MDEgy7rfm5M span{fill:#333;color:#333;}#mermaid-svg-2cSM7MDEgy7rfm5M .node rect,#mermaid-svg-2cSM7MDEgy7rfm5M .node circle,#mermaid-svg-2cSM7MDEgy7rfm5M .node ellipse,#mermaid-svg-2cSM7MDEgy7rfm5M .node polygon,#mermaid-svg-2cSM7MDEgy7rfm5M .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2cSM7MDEgy7rfm5M .rough-node .label text,#mermaid-svg-2cSM7MDEgy7rfm5M .node .label text,#mermaid-svg-2cSM7MDEgy7rfm5M .image-shape .label,#mermaid-svg-2cSM7MDEgy7rfm5M .icon-shape .label{text-anchor:middle;}#mermaid-svg-2cSM7MDEgy7rfm5M .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2cSM7MDEgy7rfm5M .rough-node .label,#mermaid-svg-2cSM7MDEgy7rfm5M .node .label,#mermaid-svg-2cSM7MDEgy7rfm5M .image-shape .label,#mermaid-svg-2cSM7MDEgy7rfm5M .icon-shape .label{text-align:center;}#mermaid-svg-2cSM7MDEgy7rfm5M .node.clickable{cursor:pointer;}#mermaid-svg-2cSM7MDEgy7rfm5M .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2cSM7MDEgy7rfm5M .arrowheadPath{fill:#333333;}#mermaid-svg-2cSM7MDEgy7rfm5M .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2cSM7MDEgy7rfm5M .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2cSM7MDEgy7rfm5M .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2cSM7MDEgy7rfm5M .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2cSM7MDEgy7rfm5M .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2cSM7MDEgy7rfm5M .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2cSM7MDEgy7rfm5M .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2cSM7MDEgy7rfm5M .cluster text{fill:#333;}#mermaid-svg-2cSM7MDEgy7rfm5M .cluster span{color:#333;}#mermaid-svg-2cSM7MDEgy7rfm5M div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2cSM7MDEgy7rfm5M .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2cSM7MDEgy7rfm5M rect.text{fill:none;stroke-width:0;}#mermaid-svg-2cSM7MDEgy7rfm5M .icon-shape,#mermaid-svg-2cSM7MDEgy7rfm5M .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2cSM7MDEgy7rfm5M .icon-shape p,#mermaid-svg-2cSM7MDEgy7rfm5M .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2cSM7MDEgy7rfm5M .icon-shape .label rect,#mermaid-svg-2cSM7MDEgy7rfm5M .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2cSM7MDEgy7rfm5M .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2cSM7MDEgy7rfm5M .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2cSM7MDEgy7rfm5M :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 负载变化
(调度器 tick / 任务切换)
governor 决定频率
(schedutil 默认)
读调度器 util
计算目标频率
driver 设置频率
(写 MSR / HWP request)
CPU 跑到新频率
一句话主线:cpufreq 是"CPU 跑多快"的状态机 ------负载变化时,由 governor 根据调度器的 CPU 利用率算出目标频率 ,再由 driver 把它写到硬件(写 MSR,或交给 HWP 硬件自主调频)。它管的是"运行中怎么调频",和 cpuidle 的"空闲时睡多深"是一对------一个管 C-state,一个管 P-state。
一、预备概念:P-state 与两个状态机
1.1 P-state:频率 + 电压的组合
P-state(Performance state)是 CPU 的一组"频率 + 电压"工作点。频率越高、电压越高,性能越强、功耗越大。x86 上 P-state 的数量和值由 CPU 型号决定(比如 P0 是最高 turbo 频率,P1 是基频,往下依次降低)。
关键:P-state 和 C-state 是两个正交的维度。C-state 是"睡多深"(空闲省电),P-state 是"跑多快"(运行时省电)。一个 CPU 可以同时处于某个 C-state(空闲)和某个 P-state(当它醒来干活时的频率档位)。
1.2 两个状态机别混(延续上一篇)
| 状态机 | 管什么 | 核心问题 |
|---|---|---|
| cpufreq(P-state) | CPU 跑多快(调频调压) | 多高的频率 |
| cpuidle(C-state) | CPU 空闲时睡多深 | 进多深的 C-state |
一句话记:cpufreq 管"醒着时跑多快",cpuidle 管"睡着时睡多深"。
二、cpufreq 框架三层:policy / driver / governor
和 cpuidle 类似,cpufreq 也是三层结构。
2.1 cpufreq_policy:一组共享时钟的 CPU
一个 policy 管一组共享同一时钟域的 CPU(同 cluster / 同 core 的 siblings),它们必须一起调频:
c
// include/linux/cpufreq.h (v6.6, line 55)
struct cpufreq_policy {
cpumask_var_t cpus; /* 共享这个 policy 的 online CPU */
unsigned int cpu; /* 管理这个 policy 的 CPU */
unsigned int min; /* 允许的最低频率(kHz) */
unsigned int max; /* 允许的最高频率(kHz) */
unsigned int cur; /* 当前频率 */
struct cpufreq_governor *governor; /* 当前 governor */
struct cpufreq_frequency_table *freq_table; /* 可用频率表 */
bool fast_switch_possible;
bool fast_switch_enabled;
// ...
};
min/max 是用户/热管理可调的边界 (/sys/.../scaling_min_freq、scaling_max_freq),governor 只能在这个范围内选频率。freq_table 是硬件支持的离散频率点。
2.2 cpufreq_driver:怎么把频率写进硬件
driver 负责"设置频率"这个底层动作,提供几种接口之一:
c
// include/linux/cpufreq.h (v6.6, line 325)
struct cpufreq_driver {
char name[CPUFREQ_NAME_LEN];
/* 二选一:老式 setpolicy,或现代的 target 系列 */
int (*setpolicy)(struct cpufreq_policy *policy);
int (*target)(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation); /* Deprecated */
int (*target_index)(struct cpufreq_policy *policy,
unsigned int index);
unsigned int (*fast_switch)(struct cpufreq_policy *policy,
unsigned int target_freq);
void (*adjust_perf)(unsigned int cpu,
unsigned long min_perf,
unsigned long target_perf,
unsigned long capacity);
// ...
};
这四种接口对应两种工作模式:
setpolicy模式 (老式):driver 自己决定频率,内核只给它两个档位------CPUFREQ_POLICY_PERFORMANCE(满频)或CPUFREQ_POLICY_POWERSAVE(最低频)。intel_pstate的 active 模式走这条。target_index/fast_switch/adjust_perf模式 (现代):governor 算出一个目标频率(或 perf),driver 把它设置到硬件。target_index是"把目标频率映射到频率表索引",fast_switch是"调度器上下文的快速切换",adjust_perf是"直接传性能等级(HWP 用)"。
2.3 cpufreq_governor:谁来决定频率
c
// include/linux/cpufreq.h (v6.6, line 577)
struct cpufreq_governor {
char name[CPUFREQ_NAME_LEN];
int (*start)(struct cpufreq_policy *policy);
void (*stop)(struct cpufreq_policy *policy);
void (*limits)(struct cpufreq_policy *policy);
// ...
};
传统 governor 有 performance(永远满频)、powersave(永远最低频)、ondemand(按采样负载调频)、conservative(缓慢调频)。而现代默认是 schedutil------它不用采样,直接用调度器的利用率信息。
三、schedutil governor:用调度器的 util 算频率
schedutil 是 v6.6 的默认 governor,它的核心思想是:调度器本来就一直在算每个 CPU 的利用率(util),直接用这个数来定频率,省掉独立的采样轮询。
3.1 核心公式:频率和 util 成线性映射
c
// kernel/sched/cpufreq_schedutil.c (v6.6, line 130)
/*
* next_freq = C * curr_freq * util_raw / max
*
* Take C = 1.25 for the frequency tipping point at (util / max) = 0.8.
*/
static unsigned int get_next_freq(struct sugov_policy *sg_policy,
unsigned long util, unsigned long max)
{
struct cpufreq_policy *policy = sg_policy->policy;
unsigned int freq = arch_scale_freq_invariant() ?
policy->cpuinfo.max_freq : policy->cur;
util = map_util_perf(util); // util 映射到 perf 域(含 1.25 余量)
freq = map_util_freq(util, freq, max); // freq = max_freq * util / max
return cpufreq_driver_resolve_freq(policy, freq); // 取最接近的可用频率
}
核心是一个线性映射 :next_freq = max_freq × util / max------CPU 利用率占最大能力的比例,就是频率占最大频率的比例。util 拉满(=max)就跑满频,util 是 50% 就跑一半频率。
那个 C = 1.25 的系数是关键细节:util 到 80% 时频率就顶到满频了,留 25% 余量。为什么?因为 util 是滞后指标------等 util 涨到 100% 才满频,负载早就过了峰值,会有性能损失。提前到 80% 就满频,能更快响应负载尖峰。
3.2 util 从哪来:effective_cpu_util
c
// kernel/sched/cpufreq_schedutil.c (v6.6, line 156)
static void sugov_get_util(struct sugov_cpu *sg_cpu)
{
unsigned long util = cpu_util_cfs_boost(sg_cpu->cpu);
struct rq *rq = cpu_rq(sg_cpu->cpu);
sg_cpu->bw_dl = cpu_bw_dl(rq); // 实时任务(DL)带宽
sg_cpu->util = effective_cpu_util(sg_cpu->cpu, util,
FREQUENCY_UTIL, NULL); // 有效利用率
}
util 来自调度器的 effective_cpu_util------它把 CFS 任务负载、RT/DL 带宽、thermal 上限 、uclamp 限制 等都折算成一个"有效利用率"。这正是 schedutil 的精髓:它和调度器是同一个视角,调度器看到多少负载,schedutil 就配多少频率,不会像 ondemand 那样"采样滞后"。
3.3 触发时机:调度器回调,不是轮询
schedutil 通过 update_util 钩子被调度器主动调用(每次 tick、任务切换、唤醒时),而不是自己定时采样:
c
// kernel/sched/cpufreq_schedutil.c (v6.6, line 331)
static void sugov_update_single_freq(struct update_util_data *hook, u64 time,
unsigned int flags)
{
// ...
if (!sugov_update_single_common(sg_cpu, time, max_cap, flags))
return; // rate limit + 读 util
next_f = get_next_freq(sg_policy, sg_cpu->util, max_cap);
/* 忙的 CPU 不急着降频(避免过早降频造成性能损失) */
if (!uclamp_rq_is_capped(cpu_rq(sg_cpu->cpu)) &&
sugov_cpu_is_busy(sg_cpu) && next_f < sg_policy->next_freq &&
!sg_policy->need_freq_update) {
next_f = sg_policy->next_freq;
}
if (sg_policy->policy->fast_switch_enabled) {
cpufreq_driver_fast_switch(sg_policy->policy, next_f); // 快速切换
} else {
sugov_deferred_update(sg_policy); // 延迟更新
}
}
两点值得注意:
- 忙时不降频:如果 CPU 一直忙、没空转,即使新算的频率更低也不降------因为"忙"说明负载还在,降频很可能是过早的,马上又要升回来。
- fast_switch vs 延迟更新 :如果 driver 支持
fast_switch,就在调度器上下文直接切换(极快);否则走 deferred update(irq_work/kthread),避免在调度器热路径做重操作。
3.4 慢路径的 worker 线程:sugov 线程
第 2 点里的"deferred update"值得展开------它背后是一个专门的 worker 线程 (kthread,名字 sugov:N)。为什么需要它?因为 sugov_update_single_freq 是在调度器持有 rq->lock 的上下文 里被调用的,此时不能直接调 driver 的 target(它可能加锁、甚至睡眠)。所以当 driver 不支持 fast_switch 时,内核用一个三层接力把"设置频率"推迟到独立线程:
c
// kernel/sched/cpufreq_schedutil.c (v6.6, line 109)
static void sugov_deferred_update(struct sugov_policy *sg_policy)
{
if (!sg_policy->work_in_progress) {
sg_policy->work_in_progress = true;
irq_work_queue(&sg_policy->irq_work); // ① 排队 irq_work,尽快离开热路径
}
}
// kernel/sched/cpufreq_schedutil.c (v6.6, line 492)
static void sugov_irq_work(struct irq_work *irq_work)
{
struct sugov_policy *sg_policy;
sg_policy = container_of(irq_work, struct sugov_policy, irq_work);
kthread_queue_work(&sg_policy->worker, &sg_policy->work); // ② 转交给 worker 线程
}
// kernel/sched/cpufreq_schedutil.c (v6.6, line 466)
static void sugov_work(struct kthread_work *work)
{
struct sugov_policy *sg_policy = container_of(work, struct sugov_policy, work);
// ...
__cpufreq_driver_target(sg_policy->policy, freq, CPUFREQ_RELATION_L); // ③ 真正调 driver
}
这个 worker 线程在 sugov_kthread_create(cpufreq_schedutil.c:579)里创建,有两个细节体现了"调频延迟敏感"的设计:
- 用
SCHED_DEADLINE调度策略(sched_setattr_nocheck),而不是普通 nice,保证它一被唤醒就尽快跑; - 绑定到
related_cpus(kthread_bind_mask),避免跑到无关 CPU 上徒增延迟。
而且它是按需创建 的------if (policy->fast_switch_enabled) return 0;(:600):driver 支持 fast_switch 就压根不建这个线程,直接走调度器上下文的 fast_switch 路径。这就是"能快切就快切,不能就交给 worker 线程"的完整闭环。
四、intel_pstate:Intel 的专用 driver
x86 上其实有两条路:老式 acpi-cpufreq(读 ACPI _PSS 表)和现代的 intel_pstate。后者是 Intel 的专用 driver,有 active / passive 两种模式,还支持 HWP。
4.1 active 模式(默认):driver 自己调频
intel_pstate 在 active 模式下走 setpolicy 接口,自己注册 update_util 回调,绕过标准 cpufreq governor:
c
// drivers/cpufreq/intel_pstate.c (v6.6, line 2574)
static int intel_pstate_set_policy(struct cpufreq_policy *policy)
{
// ...
if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) {
intel_pstate_clear_update_util_hook(policy->cpu); // 满频,不需要回调
intel_pstate_max_within_limits(cpu);
} else {
intel_pstate_set_update_util_hook(policy->cpu); // 注册 update_util 回调
}
// ...
}
static struct cpufreq_driver intel_pstate = {
.flags = CPUFREQ_CONST_LOOPS,
.setpolicy = intel_pstate_set_policy, // ← setpolicy 模式,不是 target_index
// ...
.name = "intel_pstate",
};
在调度器上下文的调频逻辑是 intel_pstate_update_util(非 HWP 时):
c
// drivers/cpufreq/intel_pstate.c (v6.6, line 2306)
static void intel_pstate_update_util(struct update_util_data *data, u64 time,
unsigned int flags)
{
// ...(iowait_boost 处理,翻倍机制)
delta_ns = time - cpu->sample.time;
if ((s64)delta_ns < INTEL_PSTATE_SAMPLING_INTERVAL)
return; // 采样间隔内不重复调
if (intel_pstate_sample(cpu, time)) // 采样(读 MSR 拿实际性能)
intel_pstate_adjust_pstate(cpu); // 调 P-state(写 MSR)
}
注意和 schedutil 的差别:intel_pstate 内部用"采样" (intel_pstate_sample 读 MSR 看实际利用率),有采样间隔限制,而不是 schedutil 那样直接用调度器 util 实时映射。active 模式是 Intel 自己的调频算法,不经过标准 governor。
4.2 HWP:硬件自己管理 P-state
现代 Intel CPU(Skylake+)支持 HWP(Hardware P-state) ------调频算法搬进硬件,硬件根据负载自主选择频率 。软件不再"指挥每一档",而是写一个 MSR_HWP_REQUEST,告诉硬件"性能范围 + 偏好":
c
// drivers/cpufreq/intel_pstate.c (v6.6, line 944)
static void intel_pstate_hwp_set(unsigned int cpu)
{
// 写 MSR_HWP_REQUEST 的 min / max / desired perf 字段
value &= ~HWP_MIN_PERF(~0L);
value |= HWP_MIN_PERF(min);
value &= ~HWP_MAX_PERF(~0L);
value |= HWP_MAX_PERF(max);
// ...
wrmsrl_on_cpu(cpu, MSR_HWP_REQUEST, value);
}
HWP 模式下,intel_pstate 的 update_util 回调主要做 hwp_boost (intel_pstate_update_util_hwp,intel_pstate.c:2158)------IO 等待或负载突增时,动态抬升 min perf 让硬件更快响应,忙完再降回去。还有一个 EPP(Energy Performance Preference) hint(MSR_HWP_REQUEST 的 bits 24-31),告诉硬件"偏性能还是偏省电",是性能/功耗偏好的粗调旋钮。
4.3 passive 模式:退回标准框架
intel_pstate=passive 会让它切换成标准 cpufreq driver(intel_cpufreq,用 .target + .fast_switch 接口),配合 schedutil 等标准 governor 工作。这个模式主要给"需要标准 cpufreq 接口/工具链"的场景用。
五、调频流程:负载变化 → 选频率 → 写硬件
把前面串起来,一次完整的调频是:
- 负载变化 :调度器 tick / 任务切换 / 唤醒,触发
update_util回调; - governor 算频率 :schedutil 读
effective_cpu_util,算next_freq = max_freq × util/max(含 1.25 余量); - driver 写硬件 :
- 通用 driver:
fast_switch(调度器上下文直接写)或target_index(映射到频率表索引写); intel_pstateHWP:写MSR_HWP_REQUEST的 min/max/desired + EPP,硬件自己落位;intel_pstate非 HWP:intel_pstate_adjust_pstate采样后写 MSR。
- 通用 driver:
对应两种调频粒度:软件调频 (软件每 tick 算一次、写一次)和 硬件调频(HWP 硬件自己快速调,软件只给范围)。
六、为什么这样设计(Why 层)
6.1 为什么有 governor,而不是固定一个频率
和 cpuidle 的 governor 同理:"跑多快"没有固定答案 。负载轻时跑满频是浪费,负载重时跑低频率是卡顿。所以必须有个决策者(governor)根据负载动态选频率。performance/powersave 是两个极端(永远满频 / 永远最低频),中间的 ondemand/schedutil 才是"动态权衡"。
6.2 为什么 schedutil 取代 ondemand
ondemand 是"定时采样 + 看负载"------它每隔一段时间读一次 CPU 负载,超过了阈值就升频。问题在于采样有滞后 :负载尖峰来了,要等下一个采样点才升频,这期间 CPU 在低频"扛"着重活。schedutil 用调度器已有的 util,负载一变调度器立刻通知,零额外采样、零滞后。这是"复用已有信息"胜过"自己另起炉灶采样"的典型例子。
6.3 为什么 intel_pstate 要专用 driver,而不是用通用 driver
通用 driver(acpi-cpufreq)通过 ACPI _PSS 表拿频率点、走标准接口,但没法利用 Intel CPU 的专属能力:
- HWP :硬件调频是 Intel 的独有能力,通用 driver 走不到;专用 driver 才能写
MSR_HWP_REQUEST把调频交给硬件。 - active 模式的高效路径:绕过标准 governor 框架,直接在调度器上下文用自己的算法调频,减少开销。
- 更精细的 P-state 控制 :
intel_pstate直接读 MSR 拿 P-state 上限、turbo 状态,通用 driver 靠 ACPI 表拿不到这些实时信息。
6.4 为什么"忙时不降频"(schedutil 里的 if 判断)
降频本身有代价(切换频率、可能 miss 缓存),如果 CPU 一直忙、负载没有实质下降,降下去马上又得升回来,来回抖动反而更差。所以 schedutil 对"还在忙的 CPU"延迟降频,等 CPU 真的空下来了才降。这是"避免频繁抖动"的阻尼设计。
七、与相邻主题的边界
| 主题 | 边界 |
|---|---|
| cpuidle(上一篇) | cpuidle 管 C-state(睡多深),cpufreq 管 P-state(跑多快)------一对正交维度 |
| CPU hotplug(#3) | hotplug 的 cpuhp_state 管"CPU 存不存在",cpufreq 管"存在的 CPU 跑多快" |
| 调度器 | schedutil 直接消费调度器的 util------调频和调度在同一视角下协同 |
附:本篇关键源码索引
| 符号 | 位置 |
|---|---|
cpufreq_policy(min/max/cur + governor) |
include/linux/cpufreq.h:55 |
cpufreq_driver(setpolicy/target_index/fast_switch/adjust_perf) |
include/linux/cpufreq.h:325 |
cpufreq_governor |
include/linux/cpufreq.h:577 |
CPUFREQ_RELATION_L/H/C(频率表查找关系) |
include/linux/cpufreq.h:284 |
get_next_freq(schedutil 算频率,含公式注释) |
kernel/sched/cpufreq_schedutil.c:139 |
sugov_get_util(读 effective_cpu_util) |
kernel/sched/cpufreq_schedutil.c:156 |
sugov_update_single_freq(触发 + fast_switch) |
kernel/sched/cpufreq_schedutil.c:331 |
sugov_update_single_perf(adjust_perf 路径,HWP) |
kernel/sched/cpufreq_schedutil.c:378 |
intel_pstate_update_util(active 模式采样调频) |
drivers/cpufreq/intel_pstate.c:2306 |
intel_pstate_set_policy / intel_pstate driver(setpolicy) |
drivers/cpufreq/intel_pstate.c:2574 / :2774 |
intel_pstate_hwp_set(写 MSR_HWP_REQUEST) |
drivers/cpufreq/intel_pstate.c:944 |