当前位置: 首页 > news >正文

Linux Schedutil 的 cached_raw_freq:频率缓存优化机制实战详解

一、简介

1.1 技术背景

Schedutil 采用事件驱动式调频,进程唤醒、任务上下文切换、中断退出都会触发cpufreq_update_util上报负载,单颗 CPU 一秒内可能产生数百次负载上报请求。 如果每一次负载推送都完整执行一遍util 归一化取值→CPU 容量修正→线性比例映射→边界钳位全套数学运算,会带来两个明显弊端:

  1. 内核 CPU 软中断开销上升:高频重复调用map_util_freq函数做浮点与乘除计算,抢占系统少量算力;
  2. 不必要的冗余变频判定:短时间内 util 负载波动极小,新计算出的目标频率和上一轮结果完全一致,重复计算没有任何业务意义。

为了削减无意义重复计算、降低 Schedutil 内核侧运行开销,内核在每一个 per-CPU 独立的struct sugov_policy结构体中新增cached_raw_freq原始频率缓存字段。 该字段用于保存上一轮经过 map_util_freq 换算、尚未被 policy 最大最小频率裁剪的原始计算频率。 当新负载抵达时,内核会先对比当前 util 算出的新原始频率与cached_raw_freq

  • 二者完全相同:直接复用缓存结果,跳过整套映射计算流程;
  • 二者存在差值:执行全新频率换算,并刷新cached_raw_freq缓存值。

简单概括核心作用:cached_raw_freq = 上一次未做边界裁剪的映射结果快照,用来命中重复负载场景,跳过重复数学计算,优化内核调频效率

很多运维与嵌入式开发者在排查 perf 采样内核耗时过高时,根源就是高并发场景下 Schedutil 频繁无缓存重复计算,而理解缓存机制就可以解释该现象,并针对性理解内核原生优化逻辑。

1.2 典型落地应用场景

  1. 高并发网关、消息队列服务器海量短连接请求造成任务频繁切换,负载上报极其密集,缓存机制大幅减少内核计算量,降低系统软中断占用;
  2. ARM 大小核嵌入式设备前台 APP 画面刷新帧率稳定,util 数值长时间小幅浮动,缓存命中后减少大核小核频繁换算功耗算力参数,省电同时降低内核开销;
  3. PREEMPT_RT 实时 Linux 控制系统减少调度周期内多余内核函数调用,缩小单次任务调度时延抖动,提升硬实时任务调度确定性;
  4. 云原生多租户混部宿主机大量 Pod 进程频繁创建销毁,负载脉冲式上报,缓存机制避免整机内核被大量调频计算挤占资源;
  5. 内核性能调优与故障定位perf 观测到sugov_update_single_freq占用 CPU 偏高时,可结合缓存机制分析是否负载抖动过于细碎,辅助业务侧优化进程调度逻辑。

1.3 学习本章核心价值

  1. 补齐 Schedutil 调频链路最后一环内核优化逻辑,完整打通「负载上报→聚合 util→缓存校验→频率映射→防抖判定→边界裁剪→硬件变频」全链路;
  2. 分清原始映射频率、缓存 raw 频率、最终生效 next_freq、硬件 cur_freq四者区别,不再混淆多层频率变量;
  3. 能够通过 perf 探针观测缓存命中与未命中场景,量化分析调频计算开销;
  4. 理解内核无锁缓存轻量化设计思路,可迁移理解其他内核子系统同类缓存优化思想;
  5. 彻底厘清 sugov_policy 内所有核心状态字段分工,从黑盒使用进阶至白盒原理调优。

二、核心概念与底层原理拆解

2.1 基础术语定义

表格

字段 / 名词归属结构体核心含义生命周期
utilsugov_policy聚合后 0~1023 归一化 CPU 负载每次调度事件实时更新
raw_freq临时变量map_util_freq 算出未经过 policy min/max 裁剪的原生目标频率单次计算临时生成
cached_raw_freqsugov_policy上一次合法计算得到的 raw_freq 缓存值常驻内核内存,覆盖式刷新
next_freqsugov_policyraw_freq 经过 policy 频率上下限钳位后的待下发频率冷却到期后更新
cur_freqsugov_policyCPU 硬件当前实际运行主频硬件驱动同步回填

2.2 cached_raw_freq 核心判定逻辑

标准执行分支伪代码:

c

运行

// 1. 根据最新util计算本次原始映射频率 new_raw = map_util_freq(util, cur_freq, policy->max, cpu_capacity); // 2. 对比缓存,命中则跳过计算刷新 if (new_raw == policy->cached_raw_freq) { // 直接沿用旧缓存结果,不再重复赋值 skip_map_calc = true; } else { // 未命中缓存,更新缓存 policy->cached_raw_freq = new_raw; } // 3. 无论是否走缓存,都对raw频率做policy边界限制,生成待生效频率next_freq policy->next_freq = clamp(policy->cached_raw_freq, policy->min, policy->max);

关键要点 1:缓存只缓存map_util_freq输出的原生 raw 频率不包含 policy 最小最大频率的裁剪步骤。 原因:policy 的 scaling_max_freq、scaling_min_freq 支持用户态随时动态修改,边界属于易变外部配置,不能存入缓存;而 util 与 cpu_capacity 相对稳定,适合做缓存键值。

关键要点 2:缓存粒度是 per-CPU 独立。每一颗逻辑 CPU 拥有专属 cached_raw_freq,核心之间缓存完全隔离,互不干扰,契合 sugov_policy 单核独立管理架构。

关键要点 3:缓存不会永久留存。当 CPU 进入长时间深度休眠、调频器重载、cpufreq_policy 被重新初始化时,cached_raw_freq 会被重置为初始无效值,强制下一次必须重新计算。

2.3 缓存命中与未命中场景举例

场景 A:缓存命中(复用计算,优化开销)

CPU 持续 50% 负载,util 稳定在 512,第一次计算 raw_freq=1200000,存入 cached_raw_freq。 后续数十次任务切换上报 util 依旧为 512,内核直接读取缓存值,不再执行乘除映射公式,直接进入边界裁剪流程。

场景 B:缓存失效(重新计算并刷新缓存)

业务流量上涨,util 从 512 提升至 768,new_raw 计算结果 = 1800000,与缓存 1200000 不一致,覆盖 cached_raw_freq 为 1800000。

场景 C:policy 参数修改强制缓存逻辑不变

手动修改 scaling_max_freq 从 2400000 改为 2000000,cached_raw_freq 仍然是 1800000;后续 clamp 钳位时会自动把 1800000 限制在新 max 上限内,缓存本身无需改动。

2.4 嵌入完整 Schedutil 调频全链路位置

  1. 任务切换 / 唤醒 →cpufreq_update_util推送负载;
  2. sugov_aggregate_util聚合 CFS/RT/IRQ 多维度负载,更新 policy->util;
  3. 读取 cached_raw_freq,尝试缓存匹配,决定是否调用 map_util_freq
  4. 匹配失败则刷新 cached_raw_freq;
  5. 校验last_freq_update_timefreq_update_delay_ns防抖冷却间隔;
  6. 对缓存 raw 频率做 policy min/max 钳位,赋值 next_freq;
  7. 冷却窗口满足 → 下发硬件变频,刷新时间戳与 cur_freq。

2.5 设计优势总结

  1. 低开销无锁缓存:仅单字段数值对比赋值,无自旋锁、无复杂数据结构,内核额外开销几乎可以忽略;
  2. 精准过滤冗余运算:仅规避完全重复的映射计算,不会造成频率更新滞后,不影响调频灵敏度;
  3. 架构解耦:缓存层与 policy 边界配置层分离,用户动态修改频率限制不会导致缓存脏数据;
  4. 原生兼容全平台:x86 对称多核、ARM 异构大小核均默认开启该缓存机制,无需内核额外开关配置。

三、环境准备

3.1 软硬件环境硬性要求

  1. 操作系统:Ubuntu 20.04/22.04、Debian 11+、CentOS Stream 8/9、嵌入式 Buildroot Linux;
  2. 内核版本:Linux 5.4 及以上正式合入 cached_raw_freq 缓存逻辑,推荐 5.15 / 6.1 LTS 长期支持内核;
  3. 硬件:具备 DVFS 动态调频的物理 x86/ARM 设备;虚拟机 CPUFreq 驱动不完整,无法观测内核缓存行为;
  4. 权限:所有内核探针、sysfs 操作必须使用 root 管理员权限。

3.2 工具一键安装

Ubuntu / Debian 系列

bash

运行

apt update -y # 调频工具、压力生成、内核探针、系统负载监控 apt install cpufrequtils stress-ng perf watch -y
CentOS / RHEL / Stream 系列

bash

运行

yum makecache fast yum install cpufrequtils stress-ng perf watch -y

3.3 工具用途说明

  1. cpufrequtils:全局切换 Schedutil 调频器,确认当前调速器运行状态;
  2. stress-ng:间歇性 / 持续性压测 CPU,制造稳定 util 负载与波动负载,分别触发缓存命中、缓存刷新;
  3. perf:挂载 kprobe 探针捕获sugov_update_single_freq内部分支执行情况,量化缓存是否生效;
  4. watch:实时监控 scaling_cur_freq 硬件频率,直观验证最终变频结果不受缓存机制影响。

3.4 前置环境校验(必执行)

bash

运行

# 1. 整机全部CPU切换至Schedutil(缓存机制仅该调速器内置) cpufreq-set -r -g schedutil # 2. 确认CPUFreq驱动正常加载 cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver # 3. 尝试挂载探针前置检查内核符号存在性 perf probe --check sugov_update_single_freq

无报错即代表内核包含该函数与 cached_raw_freq 字段逻辑,实验环境就绪。

四、分步实战案例(命令可直接复制运行)

实验一:稳定长时负载,验证缓存命中跳过重复计算

步骤 1:固定 CPU0 频率上限,消除 policy 变动干扰

bash

运行

# 锁定CPU0最大频率,避免边界变化干扰缓存观测 echo 2400000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq > /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq
步骤 2:挂载 perf 探针,标记两条分支(缓存命中 / 重新计算)

bash

运行

# 探针1:捕获未命中缓存,执行map_util_freq重新计算分支 perf probe 'sugov_update_single_freq+%if new_raw != policy->cached_raw_freq'=calc_hit # 探针2:捕获缓存完全匹配,直接复用旧值分支 perf probe 'sugov_update_single_freq+%if new_raw == policy->cached_raw_freq'=cache_hit
步骤 3:后台持续单 CPU 满载压测,生成稳定 util

bash

运行

stress-ng --cpu 1 --cpu-affinity 0 --timeout 60 &
步骤 4:录制 15 秒内核调用事件统计

bash

运行

perf record -g sleep 15 # 查看事件统计,cache_hit数量远大于calc_hit代表大量命中缓存 perf report -g none

实验现象: 稳定负载下,cache_hit(缓存复用)事件次数占比 90% 以上,calc_hit(重新映射计算)仅在最开始少量出现,证明 cached_raw_freq 成功拦截绝大多数重复计算。

实验二:间歇性启停压力,观测缓存刷新覆盖

bash

运行

# 循环启停压力,制造util从0→满→0反复切换 while true; do stress-ng --cpu 1 --cpu-affinity 0 --timeout 5 & sleep 8 done

再次使用 perf 采样可以观察到:每次压力启动 / 结束会触发一次calc_hit刷新缓存,中间稳态阶段持续命中缓存。

实验三:动态修改 scaling_max_freq,验证缓存与 policy 解耦

bash

运行

# 持续压测保持缓存生效 stress-ng --cpu 1 --cpu-affinity 0 --timeout 40 & # 中途修改最大频率上限 echo 1800000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq # 实时查看硬件频率被钳位,缓存raw值本身不发生变化 watch -n1 cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq

核心结论:policy 边界修改不会修改 cached_raw_freq 缓存内容,仅在最后 clamp 阶段约束输出频率,二者分层设计互不耦合。

实验四:单核心独立缓存验证(per-CPU 隔离)

bash

运行

# cpu0持续压测,cpu1空载 stress-ng --cpu 1 --cpu-affinity 0 --timeout 30 & # 分别给两颗CPU挂载独立探针,可观测二者缓存完全独立刷新,互不影响

cpu0 频繁触发缓存命中与刷新,cpu1 无负载 util=0,缓存仅初始化一次,证明每颗 CPU 拥有独立 cached_raw_freq。

实验收尾:清理环境,还原默认配置

bash

运行

# 终止所有压力进程 pkill stress-ng # 删除perf内核探针 perf probe -d sugov_update_single_freq # 还原CPU默认频率上限 cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq # 全局保持Schedutil默认调速器 cpufreq-set -r -g schedutil

五、常见问题与答疑(贴合实操与生产故障)

Q1:能不能在 sysfs 直接读取 / 修改 cached_raw_freq?

回答:不能。该字段属于sugov_policy内核私有成员,未导出用户态 sysfs 接口,仅在内核调度上下文内部读写,用户无法直接读写;只能通过 perf kprobe 动态捕获数值,不支持人工干预修改。

Q2:缓存会不会导致频率更新滞后、业务升频不及时?

回答:完全不会滞后。 缓存的判断依据是本次 util 计算出的 new_raw 完全等于上一次 raw;只要 util 发生任何可计算出频率差异的变化,缓存立即失效重算。 微小负载变动只要映射后频率一致才会复用缓存,不会阻塞新频率生效,不存在延迟锁死问题。

Q3:内核可以关闭 cached_raw_freq 缓存机制吗?

回答:主线 Linux 内核没有提供 sysfs 或内核启动参数开关来禁用该缓存,属于 Schedutil 内置固定优化逻辑;如需移除只能修改内核源码删除缓存对比分支后重新编译内核,常规场景不建议改动。

Q4:perf 采样发现 calc_hit(重计算)数量居高不下是什么原因?

常见两点原因:

  1. 业务进程频繁创建销毁、IO 脉冲过多,util 数值持续无规则跳变,每次映射结果都不一致,缓存无法命中;
  2. 开启了 EAS 能效调度,任务频繁跨核心迁移,单 CPU util 无法稳定,缓存频繁刷新。 优化方案:增大freq_update_delay_ns防抖间隔,过滤细碎负载抖动,提升缓存命中率。

Q5:切换 governor 调速器后缓存会怎么样?

切换调速器会销毁当前 policy 绑定的 sugov 实例,重建sugov_policy结构体,cached_raw_freq初始化为无效值,下一次负载上报强制走全新计算流程,旧缓存自动丢弃。

Q6:ARM 大小核架构下大核小核缓存会互相干扰吗?

不会。大小核各自拥有独立 policy 与 cached_raw_freq,CPU 容量 capacity 参数不同,同 util 算出的 raw_freq 本身就不同,缓存完全隔离,不会出现跨核脏数据。

六、实践建议与生产最佳实践

6.1 分场景调优配套规范

1)互联网 Web / 网关高并发服务器

默认保留原生缓存机制,将freq_update_delay_ns设置 10~15ms,过滤毫秒级毛刺负载,大幅提升 cached_raw_freq 命中比例,降低内核软中断 CPU 占用。

2)ARM 嵌入式手持 / 车载设备

前台 UI 进程负载稳定,缓存可有效减少频繁调频计算与 DVFS 切换;后台休眠进程 util 固定为 0,缓存永久命中,内核几乎无调频开销,提升续航。

3)PREEMPT_RT 硬实时系统

不建议人为改动缓存逻辑,该机制减少内核函数调用路径长度,反而有利于缩短调度时延;如需极致确定性可直接锁频,跳过整个 Schedutil 计算与缓存流程。

4)离线批量计算集群

进程长期 100% 满载,util 固定 1023,缓存一次写入永久命中,内核完全不再执行 map_util_freq 运算,推荐搭配 performance 锁频模式彻底跳过 Schedutil 模块。

6.2 内核性能排查标准思路

  1. perf top 观测sugov_update_single_freqCPU 占用偏高 → 判定缓存命中率过低;
  2. 核查业务是否存在大量短生命周期进程、频繁上下文切换;
  3. 适度拉长调频冷却延迟,聚合多次负载上报,减少计算次数;
  4. 确认无异常后台日志、定时任务高频唤醒进程造成负载抖动。

6.3 开发与内核编译注意事项

  1. 二次开发自定义 Schedutil 调速器时,建议复刻 cached_raw_freq 缓存设计,避免高频负载下内核性能损耗;
  2. 不要在 util 聚合阶段随意增加微小随机偏移,会破坏缓存等值匹配条件,导致优化失效;
  3. policy 结构体重载、CPU 热插拔场景务必重置缓存变量,防止读取无效脏数据。

6.4 运维避坑要点

  1. 无需针对该缓存做任何常态化配置,属于内核透明优化,上线基线不需要额外操作;
  2. 排查频率异常时优先排查 util、防抖、policy 边界,缓存仅为计算层优化,不会造成频率逻辑错误;
  3. 不要依赖缓存做业务侧限流,缓存属于性能优化,不是资源隔离手段,资源管控仍需依靠 cgroup 带宽配额。

七、总结与落地应用延伸

7.1 核心知识点全文复盘

  1. cached_raw_freq 本质:sugov_policy 每 CPU 私有缓存字段,存储map_util_freq未经过 policy 频率裁剪的原始映射频率,用于匹配重复负载场景,跳过冗余数学计算;
  2. 核心判定逻辑:新计算 raw 与缓存值相等则复用,不等则刷新缓存,缓存与 scaling_max/min_freq 策略边界完全解耦;
  3. 架构特性:严格 per-CPU 隔离,多核之间缓存互不干扰;调速器重载、policy 重建会清空缓存;无用户态可配置接口,内核透明运行;
  4. 链路位置:位于 util 负载聚合之后、防抖冷却判断之前,是 Schedutil 内部轻量化性能优化模块,不改变调频最终决策结果;
  5. 故障定位价值:可通过 perf 探针量化缓存命中率,辅助定位内核调频子系统 CPU 开销过高的根因。

7.2 工程落地实战价值

  1. 服务器集群功耗与内核开销优化:依托原生缓存减少软中断占用,在大流量接入场景下稳定系统基础算力;
  2. 嵌入式固件轻量化优化:减少 ARM 平台内核频繁运算,降低小核后台运行功耗,延长电池设备工作时长;
  3. 实时系统时延精简:缩短调度上下文内内核函数调用路径,降低非确定调度抖动,契合工业控制、自动驾驶主控调度需求;
  4. 内核定制开发参考范本:学习 Linux 内核无锁单值缓存的极简设计思想,可复用至各类轮询、上报类子系统开发。

7.3 全系列知识体系闭环

本文收尾 Schedutil 调频内核全链路所有核心模块,完整链条如下:

  1. 多任务负载聚合:sugov_aggregate_util+sugov_aggregate_irq
  2. 负载映射换算:map_util_freq线性比例 + 容量修正
  3. 计算缓存优化:cached_raw_freq 重复计算拦截
  4. 防抖节流控制:freq_update_delay_ns+last_freq_update_time
  5. 策略边界约束:cpufreq_policyscaling_max / scaling_min
  6. 单核心状态管理:struct sugov_policyper-CPU 全字段维护
  7. 硬件指令下发:cpufreq_driver_target驱动层变频执行

至此整套 Linux DVFS 动态电压频率调节从调度源头到硬件执行的原理与实操全部覆盖,可支撑服务器性能基线制定、嵌入式电源管理开发、内核模块二次开发、线上疑难性能故障排查等工程场景。

http://www.cnnetsun.cn/news/3435316.html

相关文章:

  • 开源项目文档模板:contributing-template与其他文档工具的集成
  • Java团队做AI开发的框架选型对比
  • LINE Bot SDK PHP推送消息与广播:实现大规模消息发送的最佳实践
  • Palantir Java Format Gradle插件完全配置指南
  • Fooocus:专注提示词工程的AI图像生成解决方案
  • Metabase开源BI工具终极指南:零代码构建企业级数据可视化平台
  • 计算机毕业设计之jsp行李寄存平台设计与实现
  • 5步创建你的专属桌面伙伴:开源虚拟宠物完整指南
  • 【法律领域】【合规领域】第一篇 法律合规 企业经营中的违法行为
  • Binder Trace实战教程:如何拦截和分析Android应用通信的终极指南
  • 终极实战指南:3步免费升级老旧Mac到最新macOS系统
  • Windows 7 SP2终极改造指南:5个步骤让经典系统重获新生
  • No!! MeiryoUI深度定制:Windows系统字体设置工具源码级扩展指南
  • HsMod:炉石传说终极体验优化插件,如何让你的游戏效率提升300%?
  • 3步掌握ARC-AGI:用抽象推理挑战你的AI智商
  • SDKmanager jetson系统及组件烧写
  • WMPageController 使用教程
  • 【这种电路的分压该如何计算?】2025-4-14
  • Mousecape:macOS光标定制终极指南,让你的鼠标指针与众不同
  • 从单调桌面到温暖陪伴:用DyberPet打造你的专属数字伙伴终极指南
  • AI编程助手融入开发流程:程序员的技能重构与实践路径
  • Binder Trace开发者指南:扩展自定义解析器和过滤器
  • 人源化小鼠模型很火,但真正难的是:如何看见那一点点“人源信号”?
  • 如何快速部署TCAS信任集群认证服务器
  • 26年9月【计算机二级】小黑课堂WPS office题库软件下载安装教程
  • 1A,5.5VIN,同步升降压恒流芯片,XZ3442
  • RetroArch语言切换完全指南:从新手到高手的本地化解决方案
  • 机器学习高级应用与系统
  • tRPC.panel()最佳实践:团队协作中的API测试与文档管理终极指南
  • 从0到1理解Optuna Dashboard架构:核心组件与实现原理解析