Linux 调度器中的带宽控制:CFS 与 RT 的 quota/period 模型

张开发
2026/4/16 20:32:48 15 分钟阅读

分享文章

Linux 调度器中的带宽控制:CFS 与 RT 的 quota/period 模型
一、简介在现代云计算、容器化和实时系统领域CPU资源的精确控制已成为系统稳定性和服务质量保障的核心技术。Linux内核通过带宽控制Bandwidth Control机制为CFS完全公平调度器和RT实时调度器两类调度类提供了基于quota配额与period周期模型的精细化CPU资源限制能力。该机制的核心价值在于在多租户云环境、容器编排平台如Kubernetes以及工业实时控制系统中防止单个任务组或进程垄断CPU资源确保关键业务获得可预期的计算资源保障。掌握这一技术对于系统管理员、云原生开发者、实时系统工程师以及学术研究人员具有重要实践意义——它不仅涉及底层内核调度原理更是现代资源隔离与QoS保障的基石。二、核心概念2.1 带宽控制的基本模型Linux调度器的带宽控制基于Token Bucket算法的变体通过两个核心参数实现参数含义单位配置路径period时间周期长度微秒(μs)cpu.cfs_period_us/sched_rt_period_usquota周期内可用CPU时间微秒(μs)cpu.cfs_quota_us/sched_rt_runtime_us计算公式CPU使用率上限 quota / period例如当period100000100ms、quota2000020ms时任务组最多只能使用20%的CPU资源。2.2 CFS带宽控制机制CFSCompletely Fair Scheduler带宽控制通过cfs_bandwidth数据结构实现主要包含period_timer高精度定时器hrtimer周期性地重置runtime配额runtime当前周期剩余可用CPU时间throttled_cfs_rq被限流的运行队列链表内核在kernel/sched/fair.c中实现关键函数sched_cfs_period_timer当定时器到期时调用__refill_cfs_bandwidth_runtime重置runtime为quota值。2.3 RT带宽控制与节流RT调度器的带宽控制称为RT Throttling实时节流其设计目标是防止实时任务因编程错误如死循环导致系统挂起。关键特性包括全局限制通过/proc/sys/kernel/sched_rt_period_us和sched_rt_runtime_us控制系统级RT任务总带宽层级分配启用CONFIG_RT_GROUP_SCHED后RT配额在cgroup层级中分配子组配额之和不得超过父组默认策略默认配置为950000/1000000即RT任务最多占用95%的CPU时间保留5%给普通任务2.4 Cgroup v1与v2的接口差异特性Cgroup v1Cgroup v2配置文件cpu.cfs_quota_us,cpu.cfs_period_uscpu.max格式quota periodBurst支持cpu.cfs_burst_us内核5.14cpu.max.burst统计信息cpu.statcpu.stat在cgroup v2中cpu.max文件格式为$MAX $PERIOD如100000 100000表示每100ms周期内最多使用100ms CPU时间即1核。三、环境准备3.1 硬件与软件要求操作系统Linux Kernel 4.18推荐5.10或6.x以获得完整特性支持架构x86_64、ARM64、RISC-V等支持cgroup的架构依赖工具# 基础工具链 sudo apt-get update sudo apt-get install -y \ linux-tools-common \ linux-tools-generic \ cgroup-tools \ stress-ng \ sysstat \ procps \ bc3.2 内核配置检查验证内核是否支持带宽控制功能# 检查CFS带宽控制支持 grep CONFIG_CFS_BANDWIDTH /boot/config-$(uname -r) # 应输出CONFIG_CFS_BANDWIDTHy # 检查RT组调度支持 grep CONFIG_RT_GROUP_SCHED /boot/config-$(uname -r) # 应输出CONFIG_RT_GROUP_SCHEDy # 检查cgroup v2支持 grep CONFIG_CGROUP_V2 /boot/config-$(uname -r) # 应输出CONFIG_CGROUP_V2y3.3 挂载cgroup文件系统# 检查当前cgroup版本 mount | grep cgroup # 如果是cgroup v1混合挂载 ls /sys/fs/cgroup/cpu/ # 应看到 cpu.cfs_period_us, cpu.cfs_quota_us 等文件 # 如果是cgroup v2统一层级 ls /sys/fs/cgroup/ # 应看到 cgroup.controllers, cgroup.subtree_control 等文件切换到cgroup v2如需# 编辑GRUB配置 sudo sed -i s/GRUB_CMDLINE_LINUX_DEFAULT/GRUB_CMDLINE_LINUX_DEFAULTsystemd.unified_cgroup_hierarchy1 / /etc/default/grub sudo update-grub sudo reboot四、应用场景在云原生多租户环境中带宽控制是容器资源隔离的核心机制。当Kubernetes设置resources.limits.cpu时实际上是通过CFS带宽控制将limit转换为cpu.cfs_quota_us与cpu.cfs_period_us的比值。例如限制为500m0.5核时实际配置为quota50000, period100000容器每100ms周期内最多获得50ms CPU时间。这在金融交易系统、视频转码集群中尤为重要——防止突发流量导致某容器耗尽节点资源影响同节点其他关键业务。在工业实时控制系统中RT带宽控制确保PLC控制任务与日志记录任务共存。通过设置sched_rt_runtime_us800000和sched_rt_period_us1000000为控制任务保留80%实时带宽同时强制20%时间用于非实时数据同步避免硬实时任务饿死内核线程导致 watchdog 超时。在学术研究与性能分析领域带宽控制提供了可重复的实验环境。研究人员可通过精确控制CPU配额量化分析不同资源约束下算法的性能边界为论文提供严谨的实验数据支撑。五、实际案例与步骤5.1 案例一CFS带宽控制基础实验Cgroup v1目标创建一个cgroup限制其CPU使用率为30%。步骤1创建cgroup并配置带宽# 创建测试cgroup sudo mkdir -p /sys/fs/cgroup/cpu/test-cgroup cd /sys/fs/cgroup/cpu/test-cgroup # 查看默认配置 cat cpu.cfs_period_us # 输出100000100ms # 设置quota为30ms即30% CPU sudo sh -c echo 30000 cpu.cfs_quota_us # 验证配置 cat cpu.cfs_quota_us # 输出30000步骤2运行压力测试并验证限流效果# 终端1启动CPU密集型任务并加入cgroup stress-ng --cpu 1 --timeout 60s PID$! echo $PID | sudo tee /sys/fs/cgroup/cpu/test-cgroup/cgroup.procs # 终端2实时监控CPU使用率应稳定在30%左右 top -p $PID # 终端3查看限流统计 watch -n 1 cat /sys/fs/cgroup/cpu/test-cgroup/cpu.stat预期输出nr_periods 1253 # 总周期数 nr_throttled 1249 # 被限流周期数 throttled_time 49823456789 # 限流等待时间纳秒步骤3动态调整配额# 提升限制至60% sudo sh -c echo 60000 cpu.cfs_quota_us # 观察top中CPU使用率变化应从30%上升至60%5.2 案例二CFS Burst功能实战内核5.14背景CPU Burst允许任务在空闲后突发使用超过配额的CPU资源提升响应性。# 检查内核版本需5.14 uname -r # 进入cgroup目录 cd /sys/fs/cgroup/cpu/burst-test # 基础配置限制为1核100ms/100ms sudo sh -c echo 100000 cpu.cfs_quota_us sudo sh -c echo 100000 cpu.cfs_period_us # 开启Burst允许突发额外2核200ms sudo sh -c echo 200000 cpu.cfs_burst_us # 查看统计需运行负载后 cat cpu.stat # 输出应包含 # nr_burst 15 # 触发burst次数 # burst_time 23456789012 # 突发使用时间5.3 案例三Cgroup v2带宽控制目标使用统一层级接口限制容器CPU。# 创建cgroup v2子组 sudo mkdir -p /sys/fs/cgroup/mycontainer cd /sys/fs/cgroup/mycontainer # 启用CPU控制器 sudo sh -c echo cpu /sys/fs/cgroup/cgroup.subtree_control # 设置限制每100ms最多使用50ms0.5核 # 格式quota period-1表示无限制 sudo sh -c echo 50000 100000 cpu.max # 设置burst允许突发至1.5核 sudo sh -c echo 50000 cpu.max.burst # 将进程加入cgroup echo $$ | sudo tee cgroup.procs # 运行测试 stress-ng --cpu 2 --timeout 30s5.4 案例四RT调度器带宽控制目标配置系统级RT任务带宽并创建RT cgroup。步骤1查看并修改全局RT配置# 查看当前RT带宽设置 cat /proc/sys/kernel/sched_rt_period_us # 输出10000001秒 cat /proc/sys/kernel/sched_rt_runtime_us # 输出9500000.95秒 # 修改配置保留10%给非实时任务 sudo sh -c echo 900000 /proc/sys/kernel/sched_rt_runtime_us # 持久化配置 echo kernel.sched_rt_runtime_us 900000 | sudo tee -a /etc/sysctl.conf sudo sysctl -p步骤2创建RT任务并测试节流# 编译测试程序实时循环 cat rt_test.c EOF #include stdio.h #include stdlib.h #include sched.h #include unistd.h int main() { struct sched_param param { .sched_priority 80 }; if (sched_setscheduler(0, SCHED_FIFO, param) 0) { perror(sched_setscheduler); return 1; } printf(RT task running (PID: %d)\n, getpid()); while (1); // 死循环测试节流 return 0; } EOF gcc -o rt_test rt_test.c # 运行RT任务将被限制在90% CPU sudo chrt -f 80 ./rt_test RT_PID$! # 监控应看到CPU使用率在90% plateau top -p $RT_PID步骤3RT cgroup层级配置需CONFIG_RT_GROUP_SCHED# 创建RT cgroup sudo mkdir -p /sys/fs/cgroup/cpu/rt-group cd /sys/fs/cgroup/cpu/rt-group # 配置RT配额注意需小于父组可用配额 sudo sh -c echo 1000000 cpu.rt_period_us sudo sh -c echo 400000 cpu.rt_runtime_us # 40% RT带宽 # 验证 cat cpu.rt_runtime_us5.5 案例五编程接口——使用systemd cgroup API/* * cgroup_bandwidth_control.c * 演示通过libcgroup API编程控制带宽 */ #include stdio.h #include stdlib.h #include string.h #include libcgroup.h int setup_cfs_bandwidth(const char *group_name, unsigned long quota_us, unsigned long period_us) { int ret; struct cgroup *cgrp; struct cgroup_controller *cpu_ctrl; // 初始化libcgroup ret cgroup_init(); if (ret ! 0) { fprintf(stderr, cgroup_init failed: %s\n, cgroup_strerror(ret)); return -1; } // 创建cgroup实例 cgrp cgroup_new_cgroup(group_name); if (!cgrp) { fprintf(stderr, Failed to create cgroup\n); return -1; } // 添加CPU控制器 cpu_ctrl cgroup_add_controller(cgrp, cpu); if (!cpu_ctrl) { fprintf(stderr, Failed to add CPU controller\n); cgroup_free(cgrp); return -1; } // 设置CFS配额和周期 ret cgroup_set_value_ulong64(cpu_ctrl, cpu.cfs_quota_us, quota_us); if (ret ! 0) { fprintf(stderr, Failed to set quota: %s\n, cgroup_strerror(ret)); goto err; } ret cgroup_set_value_ulong64(cpu_ctrl, cpu.cfs_period_us, period_us); if (ret ! 0) { fprintf(stderr, Failed to set period: %s\n, cgroup_strerror(ret)); goto err; } // 创建并应用配置 ret cgroup_create_cgroup(cgrp, 0); if (ret ! 0) { fprintf(stderr, Failed to create cgroup: %s\n, cgroup_strerror(ret)); goto err; } printf(Successfully configured %s: quota%lu, period%lu\n, group_name, quota_us, period_us); err: cgroup_free(cgrp); return ret; } int main(int argc, char **argv) { if (argc ! 4) { fprintf(stderr, Usage: %s group_name quota_us period_us\n, argv[0]); return 1; } return setup_cfs_bandwidth(argv[1], atol(argv[2]), atol(argv[3])); }编译与运行# 安装依赖 sudo apt-get install libcgroup-dev # 编译 gcc -o cgroup_bandwidth cgroup_bandwidth_control.c -lcgroup # 运行创建限制为0.5核的cgroup sudo ./cgroup_bandwidth myapp-group 50000 100000 # 验证 cat /sys/fs/cgroup/cpu/myapp-group/cpu.cfs_quota_us六、常见问题与解答Q1为什么设置了quota但CPU使用率仍超过限制原因CFS带宽控制是周期性的如果任务在周期内未用完quota剩余时间不会累积到下一周期。短周期如10ms的突发负载可能导致观测到的瞬时CPU使用率超过限制但长期平均会符合quota/period比例。验证方法# 使用mpstat观察多周期平均 mpstat -P ALL 1 10 | grep AverageQ2RT任务被节流导致性能下降如何排查诊断步骤# 1. 检查是否触发RT节流 grep sched: RT throttling activated /var/log/kern.log # 2. 查看当前RT带宽使用 cat /proc/sys/kernel/sched_rt_runtime_us cat /proc/sys/kernel/sched_rt_period_us # 3. 临时禁用节流仅调试生产环境慎用 sudo sh -c echo -1 /proc/sys/kernel/sched_rt_runtime_us注意禁用RT节流后实时任务可能饿死内核线程导致系统不稳定。Q3Kubernetes中CPU limit导致应用延迟飙升根本原因Kubernetes的CPU limit通过CFS quota实现。当容器在period早期耗尽quota后会被throttle直到下一周期造成周期性卡顿。解决方案使用CPU Burst内核5.14# 在Pod注解中启用burst需节点支持 annotations: cpu.cfs_burst_us: 100000设置合理的request与limit比例避免limit过低考虑使用CPU pinningcpuset替代limit减少调度开销Q4写入sched_rt_runtime_us时报错Device or resource busy原因内核5.x后为保证公平调度器DL server的最小带宽要求sched_rt_runtime_us/sched_rt_period_us 0.05即5%。解决# 错误示例小于5% echo 49999 /proc/sys/kernel/sched_rt_runtime_us # 失败 # 正确配置至少保留5%给非RT任务 echo 50000 /proc/sys/kernel/sched_rt_runtime_us # 成功Q5如何监控带宽控制导致的限流事件监控脚本#!/bin/bash # cgroup_throttle_monitor.sh - 监控cgroup限流事件 CGROUP_PATH/sys/fs/cgroup/cpu INTERVAL5 while true; do for cgroup in $(find $CGROUP_PATH -type d -name docker-* 2/dev/null); do if [ -f $cgroup/cpu.stat ]; then nr_throttled$(grep nr_throttled $cgroup/cpu.stat | awk {print $2}) if [ $nr_throttled -gt 0 ]; then echo [$(date)] Throttle detected in $(basename $cgroup): $nr_throttled periods fi fi done sleep $INTERVAL done七、实践建议与最佳实践7.1 调试技巧1. 使用ftrace跟踪调度事件# 启用调度跟踪 sudo mount -t debugfs none /sys/kernel/debug echo 1 /sys/kernel/debug/tracing/events/sched/sched_stat_throttled/enable cat /sys/kernel/debug/tracing/trace_pipe | grep throttled2. 分析throttle时间分布# 使用bpftrace获取细粒度统计 sudo bpftrace -e tracepoint:sched:sched_stat_throttled { throttle_ns[comm] hist(args-delay); } 7.2 性能优化1. 合理选择period长度短period1-10ms低延迟应用但调度开销增加长period100-1000ms批处理应用减少上下文切换但突发响应差2. 避免quota碎片化# 不推荐quota 1ms调度器开销占比过高 echo 1000 cpu.cfs_quota_us # 推荐最小quota至少为1ms echo 10000 cpu.cfs_quota_us # 至少10ms3. 结合cpuset优化# 将任务绑定到特定CPU减少缓存失效 sudo sh -c echo 0-3 /sys/fs/cgroup/cpuset/mygroup/cpuset.cpus7.3 生产环境配置模板Web服务容器延迟敏感# 限制1核允许突发至2核 echo 100000 100000 cpu.max # 基础限制1核 echo 100000 cpu.max.burst # 突发1核 echo 100 cpu.weight # 相对权重批处理任务吞吐量优先# 长周期高配额减少调度干扰 echo 1000000 cpu.cfs_period_us # 1秒周期 echo 800000 cpu.cfs_quota_us # 80% CPU echo 0 cpu.cfs_burst_us # 禁用burst严格限制八、总结与应用场景本文深入剖析了Linux调度器中CFS与RT调度类的带宽控制机制从内核cfs_bandwidth数据结构的实现原理到cgroup v1/v2的用户态接口再到Kubernetes等云原生场景的实际应用。通过quota/period模型系统管理员和开发者能够实现多租户资源隔离防止容器/虚拟机资源抢占实时任务保护确保关键任务获得确定性CPU时间能耗与性能平衡通过动态调整配额适应负载变化在学术研究中这些机制为实时系统调度算法评估、云资源调度策略优化提供了可量化的实验平台。建议读者结合本文提供的代码示例在测试环境中进行系统性实验并参考内核源码kernel/sched/fair.c和kernel/sched/rt.c深入理解实现细节。掌握Linux调度器带宽控制技术不仅是系统调优的必备技能更是理解现代操作系统资源管理本质的关键窗口。

更多文章