一、简介在现代 Linux 系统中CPU 动态调频调压DVFS是平衡性能、功耗、发热三大指标的核心技术而schedutil作为 Linux 内核主流的 CPU 调频策略完全依托调度子系统的负载统计结果完成频率决策也是目前服务端、嵌入式、工控、车载实时系统的默认调频方案。传统调频方案如ondemand、performance仅依靠简单的 CPU 空闲率判断负载存在判断粗糙、响应滞后、无法区分任务类型等缺陷。而schedutil深度耦合调度器通过PELT 负载统计框架精准采集整机 CPU 负载并通过sugov_aggregate_util核心函数完成CFS 普通进程、RT 实时进程、DL 截止时间进程、IRQ 中断四类负载的统一聚合计算最终输出真实、客观的 CPU 综合利用率指导内核选择最优 CPU 运行频率。从工程落地角度来看该模块广泛应用于三大场景第一是工业实时控制系统工控设备混合运行普通业务、硬实时控制任务、硬件中断要求调频策略不能因负载误判导致实时任务丢截止期第二是车载 Linux 系统车机同时运行多媒体应用、车身控制实时任务、传感器中断需兼顾功耗与实时性第三是边缘计算嵌入式设备设备算力有限、电池供电依赖精细负载聚合实现低功耗运行。对于底层开发者、内核调优工程师、系统架构师而言吃透sugov_aggregate_util负载聚合逻辑不仅能理解 Linux 调度与功耗子系统的联动原理还能解决线上调频异常、实时任务卡顿、功耗过高、CPU 降频卡顿等疑难问题同时也是撰写 Linux 内核调度、功耗优化相关论文、技术报告的核心知识点。本文将从概念、环境、源码、实操、排错全维度拆解schedutil负载聚合流程所有代码、命令均可直接复现。二、核心概念想要理解schedutil负载聚合逻辑必须先掌握调度类、PELT、util 利用率、调度域、DVFS 等基础术语本节结合内核实际运行逻辑逐一说明避免纯理论堆砌。2.1 Linux 四大核心调度类Linux 内核将系统任务划分为不同调度类不同调度类优先级、调度策略、负载统计逻辑各不相同也是sugov_aggregate_util需要聚合的核心负载来源CFS 调度类完全公平调度系统默认调度类负责普通用户进程、后台服务、桌面应用等非实时任务采用红黑树 虚拟运行时间实现公平抢占是系统中数量最多的任务类型其负载记为util_cfs。RT 调度类实时调度面向软实时任务包含SCHED_FIFO、SCHED_RR两种策略优先级高于 CFS常见于音视频采集、简单设备控制任务负载记为util_rt。DL 调度类截止时间调度硬实时调度类遵循 sporadic 任务模型严格按照任务截止时间调度优先级最高广泛用于工业控制、车载自动驾驶等硬实时场景负载记为util_dl。IRQ 中断负载硬件中断、软中断产生的瞬时 CPU 占用不属于进程范畴但会直接消耗 CPU 算力是负载聚合中不可忽略的部分负载记为util_irq。2.2 PELT 负载统计框架PELTPer-Entity Load Tracking实体级负载追踪是 Linux 调度器的负载统计基石schedutil 所有利用率数据均来自 PELT。其核心原理是指数加权移动平均EWMA以 1024μs 为统计周期对历史负载做衰减计算既保留瞬时负载特征又避免瞬时尖峰导致调频抖动。PELT 输出两个核心指标running任务实际占用 CPU 的时间占比runnable任务处于就绪队列、等待 CPU 的时间占比。schedutil优先采用running指标作为基础利用率同时支持util_est预估优化解决周期性任务休眠唤醒后负载统计滞后问题。2.3 util 利用率 sugov 架构util 利用率内核用[0, 1024]定标值表示 CPU 利用率0代表 CPU 完全空闲1024代表 CPU 满载该定标规则贯穿schedutil全流程。sugov全称Schedutil Governor是schedutil调频策略的核心管理单元每个 CPU 调度域对应一个sugov实例sugov_aggregate_util就是sugov内部负责多调度类负载汇总的核心函数。DVFSCPU 动态调频调压内核根据sugov输出的综合利用率查询硬件 OPP操作性能点切换 CPU 主频与电压。2.4 关键函数定位本文核心分析函数sugov_aggregate_util()定义于内核文件kernel/sched/cpufreq_schedutil.c作用是遍历当前 CPU 运行队列累加 CFS、RT、DL、IRQ 四类负载计算 CPU 综合有效利用率。三、环境准备本节给出可复现的软硬件环境、内核版本、工具链配置所有环境均为工业界主流版本读者可基于该环境完成源码阅读、编译、调试、实操验证。3.1 硬件环境架构x86_64 / ARM64推荐 x86_64调试工具更完善CPU至少 2 核支持 DVFS 调频主流 Intel/AMD/ARM 处理器均支持内存≥4GB磁盘≥20GB存放内核源码、编译产物3.2 软件环境3.2.1 操作系统与内核版本推荐两个稳定版本源码逻辑一致sugov_aggregate_util无大幅改动测试系统Ubuntu 20.04 LTS / Ubuntu 22.04 LTS桌面 / 服务器版均可内核版本Linux 5.10 LTS工业嵌入式、服务器主流长期支持版本、Linux 5.15 LTS车载、边缘设备主流版本说明Linux 4.19 及更早版本schedutil逻辑略有差异本文以 5.10 标准逻辑为准。3.2.2 依赖工具安装可直接复制执行执行以下命令安装编译、调试、内核跟踪、源码阅读全套工具# 更新软件源 sudo apt update sudo apt upgrade -y # 安装内核编译依赖、开发库 sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev -y # 安装调试、跟踪工具ftrace、perf、gdb、readelf sudo apt install trace-cmd perf gdb binutils dwarves -y # 安装源码阅读工具、文本编辑工具 sudo apt install cscope ctags vim git -y3.2.3 内核源码获取与解压# 创建工作目录 mkdir -p ~/linux_work cd ~/linux_work # 拉取Linux 5.10 LTS 官方源码 git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git linux-5.10 cd linux-5.10 git checkout v5.10.2103.2.4 内核配置开启调试与 Schedutil 相关选项需要开启调度调试、PELT 统计、Schedutil 调频、Ftrace 跟踪用于后续代码调试与实操# 复制当前系统内核配置 cp -v /boot/config-$(uname -r) .config # 打开图形化配置界面 make menuconfig需手动开启以下配置项路径逐级查找General setup→Kernel debugging开启所有调试选项Kernel hacking→Sched Debugging开启SCHED_DEBUG、SCHED_STACK_END_CHECKCPU Power Management→CPU Frequency scaling开启CPU Frequency scaling开启schedutil cpufreq governor默认选中保证调频策略为 schedutilKernel hacking→Tracers开启Ftrace、Function tracer、Function graph tracer用于跟踪内核函数调用。配置完成后保存退出执行编译编译耗时根据 CPU 性能决定# 多核编译-j 后接CPU核心数示例4核 make -j4 # 安装内核模块 新内核 sudo make modules_install sudo make install重启系统选择新编译的 5.10 内核启动。3.3 环境验证重启后执行以下命令验证环境是否就绪# 1. 查看当前内核版本 uname -r # 预期输出5.10.210 # 2. 查看当前CPU调频策略必须为schedutil cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor # 预期输出schedutil # 3. 查看Ftrace是否可用 ls /sys/kernel/debug/tracing/ # 存在目录即代表ftrace正常以上三条命令全部验证通过代表环境准备完成。四、应用场景300 字schedutil多调度类负载聚合主要应用于混合负载的实时嵌入式与工控系统。在工业 PLC 控制设备中系统同时运行 DL 硬实时控制任务、RT 设备采集任务、CFS 后台日志服务与硬件传感器 IRQ 中断sugov_aggregate_util聚合全类型负载避免单一负载漏判导致 CPU 降频保障实时任务运行。在车载信息娱乐系统中车机 CFS 多媒体进程、RT 蓝牙音频任务、车身总线中断混合运行精准负载聚合可在播放音视频时不降频、待机时自动降频省电。在边缘网关设备中大量网络软中断、数据转发实时任务与运维后台进程共存该功能保证网关在高负载下稳定调频杜绝因负载统计不准引发的网络卡顿、数据丢包问题。五、实际案例与步骤源码解析 实操代码本节分为内核源码逐行解析、命令行实操验证、负载模拟测试三部分所有代码、命令均可直接复制运行结合注释讲解逻辑。5.1 核心函数 sugov_aggregate_util 源码解析文件路径linux-5.10/kernel/sched/cpufreq_schedutil.c该函数核心作用遍历单个 CPU 运行队列rq依次累加 CFS、RT、DL、IRQ 四类负载计算综合 util 值作为调频依据。5.1.1 函数整体框架与完整源码带详细注释/** * sugov_aggregate_util - 聚合单个CPU运行队列的所有调度类负载 * sg: sugov 实例对应一个CPU调度域 * util: 输出参数最终聚合后的综合利用率 [0,1024] * 返回值bool是否存在高优先级实时负载 */ static bool sugov_aggregate_util(struct sugov *sg, unsigned int *util) { // 定义局部变量运行队列、各类负载值、CPU容量 struct rq *rq sg-rq; unsigned int util_cfs 0; // CFS普通进程负载 unsigned int util_rt 0; // RT实时进程负载 unsigned int util_dl 0; // DL硬实时进程负载 unsigned int util_irq 0; // 中断负载 unsigned int cpu_capacity; // CPU最大容量固定1024 // 1. 获取CPU额定容量内核定标为1024满载 cpu_capacity capacity_of(rq-cpu); // 2. 读取CFS调度类负载rq-cfs.runnable_load_avg 来自PELT统计 util_cfs rq-cfs.runnable_load_avg; // 3. 读取RT调度类负载 util_rt rq-rt.rt_load_avg; // 4. 读取DL调度类负载 util_dl rq-dl.dl_load_avg; // 5. 读取软中断、硬中断负载 util_irq irq_load_avg(rq); /* * 核心聚合逻辑累加所有负载 * 规则负载总和不能超过CPU最大容量1024防止溢出 */ *util util_cfs util_rt util_dl util_irq; if (*util cpu_capacity) *util cpu_capacity; /* * 判断是否存在高优先级实时负载RT/DL * 若存在schedutil会直接拉满CPU频率保障实时性 */ return (util_rt 0) || (util_dl 0); }5.1.2 源码逻辑逐段解读数据来源所有xxx_load_avg变量均由 PELT 框架实时更新任务唤醒、阻塞、CPU 时间片耗尽时都会触发 PELT 计算保证负载数据实时性负载累加规则简单线性累加四大类负载这是schedutil最核心的设计不再区分任务类型只看整体 CPU 占用溢出保护多类负载叠加可能超过 1024理论满载值因此做上限截断统一限制为cpu_capacity实时负载特殊处理只要 RT/DL 负载不为 0函数返回 true上层调频逻辑会直接设置 CPU 为最高频率优先保障实时任务这也是实时 Linux 的关键特性。5.2 配套内核调用链路梳理sugov_aggregate_util不会单独执行其完整调用链路如下帮助读者理解触发时机# 触发场景任务切换、任务唤醒、中断退出、定时采样 sched_class::task_tick / wakeup_new_task → sugov_update_single() → sugov_aggregate_util() // 核心负载聚合 → sugov_frequency_update() // 根据聚合结果计算目标频率 → cpufreq_driver_target() // 下发调频指令到硬件触发时机总结只要调度器状态发生变化就会触发负载聚合与调频响应延迟微秒级。5.3 实操一使用 Ftrace 跟踪 sugov_aggregate_util 调用通过 ftrace 跟踪函数调用验证函数运行时机、调用频率是内核调试常用手段。步骤 1挂载 debugfs部分系统默认未挂载# 挂载debugfsftrace依赖该文件系统 sudo mount -t debugfs none /sys/kernel/debug步骤 2开启函数跟踪指定跟踪目标函数# 切换到ftrace目录 cd /sys/kernel/debug/tracing # 清空历史跟踪日志 sudo echo trace # 设置跟踪器为function函数调用跟踪 sudo echo function current_tracer # 只跟踪 sugov_aggregate_util 函数 sudo echo sugov_aggregate_util set_ftrace_filter # 开启跟踪 sudo echo 1 tracing_on步骤 3模拟负载观察调用日志新开终端使用stress工具模拟 CFS 高负载# 安装压力测试工具 sudo apt install stress -y # 模拟2核CPU满载CFS任务 stress --cpu 2 回到 ftrace 终端查看跟踪日志# 查看跟踪输出 sudo cat trace现象说明日志中会持续出现sugov_aggregate_util调用记录证明 CFS 负载变化会频繁触发负载聚合。步骤 4停止跟踪结束压力测试# 关闭跟踪 sudo echo 0 tracing_on # 结束stress进程 killall stress5.4 实操二读取内核负载变量验证聚合数值直接读取运行队列的各类load_avg值手动计算聚合结果和内核输出对比。步骤 1编写简易内核模块读取 rq 负载数据创建文件load_read.c代码如下可直接复制#include linux/init.h #include linux/module.h #include linux/kernel.h #include linux/sched.h #include linux/sched/cpufreq_schedutil.h MODULE_LICENSE(GPL); MODULE_AUTHOR(Linux Engineer); MODULE_DESCRIPTION(Read rq load avg for schedutil test); static int __init load_read_init(void) { struct rq *rq; int cpu 0; // 读取cpu0的运行队列 // 获取指定CPU的运行队列 rq cpu_rq(cpu); // 打印四类原始负载 pr_info(CPU%d CFS load: %u\n, cpu, rq-cfs.runnable_load_avg); pr_info(CPU%d RT load: %u\n, cpu, rq-rt.rt_load_avg); pr_info(CPU%d DL load: %u\n, cpu, rq-dl.dl_load_avg); pr_info(CPU%d IRQ load: %u\n, cpu, irq_load_avg(rq)); // 手动聚合计算综合util unsigned int total rq-cfs.runnable_load_avg rq-rt.rt_load_avg rq-dl.dl_load_avg irq_load_avg(rq); if(total 1024) total 1024; pr_info(CPU%d Total aggregate util: %u\n, cpu, total); return 0; } static void __exit load_read_exit(void) { pr_info(Load read module unload\n); } module_init(load_read_init); module_exit(load_read_exit);步骤 2编写 Makefile创建Makefile文件obj-m load_read.o KERNELDIR ? /home/你的用户名/linux_work/linux-5.10 PWD : $(shell pwd) all: $(MAKE) -C $(KERNELDIR) M$(PWD) modules clean: $(MAKE) -C $(KERNELDIR) M$(PWD) clean步骤 3编译、加载模块查看内核日志# 编译模块 make # 加载内核模块 sudo insmod load_read.ko # 查看内核打印日志 dmesg | tail -10结果验证日志中会输出四类独立负载 手动聚合的总负载与sugov_aggregate_util计算逻辑完全一致。步骤 4卸载模块sudo rmmod load_read make clean5.5 实操三模拟 RT/DL 实时负载验证实时优先级逻辑本实验验证当存在 RT/DL 任务时sugov_aggregate_util返回 trueCPU 直接拉满主频。步骤 1创建 RT 实时任务C 语言测试程序创建rt_test.c编译运行实时任务#include stdio.h #include stdlib.h #include pthread.h #include sched.h void *rt_thread(void *arg) { // 设置线程为SCHED_FIFO 实时策略 struct sched_param param; param.sched_priority 50; pthread_setschedparam(pthread_self(), SCHED_FIFO, param); // 死循环占用CPU模拟RT负载 while(1); return NULL; } int main() { pthread_t tid; pthread_create(tid, NULL, rt_thread, NULL); pthread_join(tid, NULL); return 0; }编译并运行需要 root 权限gcc rt_test.c -o rt_test -lpthread sudo ./rt_test 步骤 2查看 CPU 当前频率# 持续查看CPU0频率 watch -n1 cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq实验现象CPU 频率立即拉升至硬件最大频率证明 RT 负载触发了实时优先逻辑。步骤 3结束测试进程killall rt_test六、常见问题与解答结合多年内核调优、线上故障排查经验整理实操与源码学习中最高频的问题全部对应上文步骤与代码。Q1执行 stress 压测后ftrace 抓不到 sugov_aggregate_util 调用原因1.debugfs 未挂载2.ftrace 未正确开启3. 内核未启用schedutil调频策略。解决方案执行sudo mount -t debugfs none /sys/kernel/debug手动挂载重新检查内核配置确保开启Ftrace与schedutil governor执行cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor确认策略为schedutil。Q2自定义内核模块编译时报错 “隐式声明 irq_load_avg”原因irq_load_avg是内核内部函数未对外导出符号。解决方案仅用于学习调试可在内核源码irq_load_avg函数处添加导出宏EXPORT_SYMBOL_GPL(irq_load_avg);重新编译内核即可。Q3聚合后的 util 值超过 1024是否正常回答正常。多调度类负载叠加CFSRTIRQ会出现数值溢出sugov_aggregate_util内部做了截断最终 util 上限固定为 1024不会影响调频逻辑。Q4系统只有 CFS 任务时RT/DL 负载始终为 0是否正常回答完全正常。普通桌面、服务器默认只运行 CFS 任务RT/DL 调度类仅被显式设置为实时策略的进程使用。Q5开启 RT 实时任务后CPU 频率不会下降如何恢复原因RT 任务存在时函数返回 true强制 CPU 跑满主频。解决方案杀死所有SCHED_FIFO/SCHED_RR实时进程频率会自动回落。Q6PELT 负载数值波动很大是否代表代码异常回答正常。PELT 基于 EWMA 动态衰减任务唤醒、休眠、中断触发都会导致负载瞬时变化schedutil依靠这种动态变化实现快速调频。七、实践建议与最佳实践结合工业项目落地、内核调优、问题排查场景给出调试、优化、避坑的实战建议分为调试技巧、性能优化、工程避坑三部分。7.1 调试技巧分层排查负载问题当出现调频异常时先通过dmesg、自定义模块读取 CFS/RT/DL/IRQ 四类独立负载定位是哪一类负载导致聚合结果异常不要直接修改调频参数。Ftrace 精准过滤线上环境不能全局跟踪使用set_ftrace_pid指定进程 PID 跟踪减少性能开销。结合 Perf 采样使用perf record -g sleep 10采样 CPU 热点结合schedutil负载数据判断是负载高还是调频策略不合理。7.2 性能优化最佳实践实时系统优化工业、车载硬实时系统中若存在 DL/RT 任务建议保留schedutil实时优先逻辑不要关闭避免实时任务被降频打断。高吞吐服务器优化纯 CFS 业务服务器可适当调整schedutil调频采样周期减少频繁调频带来的硬件开销。低功耗嵌入式优化边缘设备、电池供电设备不要人为拉高负载阈值依靠原生聚合逻辑实现动态降频最大化续航。7.3 工程避坑要点禁止修改负载聚合公式util_cfs util_rt util_dl util_irq是内核标准逻辑私自修改会导致负载统计失真引发调频紊乱。区分 CPU 架构差异ARM 架构 DVFS 域与 x86 不同多核心 ARM 芯片需注意调度域对应的sugov实例不要单 CPU 测试直接套用多核逻辑。内核版本兼容Linux 4.19 及更早版本sugov_aggregate_util函数入参、变量名略有差异移植代码时需做版本适配。压力测试规范测试负载聚合时分开压测 CFS、RT、IRQ 三类负载不要混合压测便于定位问题。八、总结与拓展应用场景8.1 全文要点回顾本文从背景、概念、环境、源码、实操、排错全链路解析了schedutil的util聚合核心函数sugov_aggregate_util核心知识点总结如下schedutil是 Linux 主流 DVFS 调频策略完全依赖调度器 PELT 负载统计sugov_aggregate_util负责聚合 CFS、RT、DL、IRQ 四大类负载采用线性累加 溢出截断逻辑函数具备实时任务优先特性只要 RT/DL 负载存在CPU 直接拉满主频保障硬实时业务所有负载数据来自运行队列rq的xxx_load_avg成员由 PELT 框架实时更新实操可通过 Ftrace、自定义内核模块、压力测试工具完成函数调用、数值、逻辑验证。8.2 拓展应用场景与落地建议该模块的学习成果可直接落地到多类工业场景也是内核研发、系统调优、论文撰写的核心支撑工业实时控制系统基于负载聚合逻辑排查实时任务卡顿、CPU 降频问题优化工控设备稳定性车载 Linux 系统开发车机混合多媒体、车身实时任务依托schedutil负载特性做功耗与实时性平衡嵌入式边缘设备根据聚合负载曲线制定硬件 OPP 调频表优化设备功耗内核二次开发基于原生聚合逻辑定制差异化调频策略如行业专属低延迟调频学术研究与报告PELT 负载统计、多调度类负载聚合、调度与功耗子系统联动是 Linux 内核方向论文、技术报告的优质研究点。对于底层开发者而言调度子系统与功耗子系统是 Linux 内核两大核心模块二者的联动是系统优化的重中之重。建议读者在本文实操基础上继续深入阅读 PELT 源码、cpufreq 驱动框架将理论知识结合线上问题、项目需求持续打磨真正做到吃透原理、落地实战。