Linux 组调度的 cfs_bandwidth 结构体:带宽控制的核心配置 简介在 Linux CFS 完全公平调度体系中传统基于 nice 权重的调度仅能实现 CPU 时间按比例均分无法对控制组cgroup v1/cgroup v2做硬性 CPU 使用上限约束。随着容器技术 Docker、K8s 大规模落地云主机资源配额隔离、嵌入式多业务分区运行、工控多进程资源隔离等场景迫切需要对一组进程整体限制最大 CPU 消耗CFS 带宽控制器就是依托 cfs_bandwidth 结构体实现配额管控。cfs_bandwidth 是内核kernel/sched/fair.c中 CFS 组调度带宽限流的核心数据载体所有 CPU 周期配额、剩余可用时间、突发扩容、定时器触发、节流等待队列等配置全部收纳在此结构体。内核依靠该结构体记录周期窗口时长、组最大可用 CPU 时间当组内所有进程累计占用 CPU 超出配额后触发节流throttle任务挂入等待队列暂停调度直到新周期刷新配额后解除限流。对于内核开发、容器底层研发、嵌入式实时系统调优、云计算运维工程师而言吃透cfs_bandwidth各个成员字段含义、字段变更时机、内核运行逻辑是排查容器 CPU 限额失效、业务莫名卡顿、突发带宽配置不生效、CFS 节流抖动等疑难问题的必备能力同时也是自研资源隔离框架、撰写调度相关毕业论文、定制内核 CPU 管控补丁的核心理论支撑。本文从基础概念、环境编译、源码拆解、用户态实操、故障排查全链路落地讲解所有示例代码与调试命令均可直接上机复现。一、核心概念与术语解析1.1 CFS 带宽限流基础模型CFS 带宽控制采用周期 配额二元管控模型挂载在 CPU cgroup 子系统quota配额一个周期内该 cgroup 分组全部进程总共能使用的 CPU 总时间单位微秒 usperiod周期带宽统计的统计窗口时长单位微秒 us默认 100ms 即 100000us例quota20000period100000代表该分组在每 100ms 周期内最多占用单个 CPU 20ms 时间等效限制 CPU 使用率上限 20%。突发带宽burstLinux 5.13 之后新增字段允许分组在空闲时积攒富余 CPU 时间业务瞬时峰值短时间超配额运行解决流量毛刺带来的频繁节流卡顿。1.2 调度实体层级task_group 与 cfs_bandwidth 绑定关系内核使用struct task_group表征一个 CPU 控制组每个 task_group 内嵌struct cfs_bandwidth成员struct task_group { struct cfs_bandwidth cfs_b; /* 当前分组带宽配置主体 */ struct sched_entity **se; struct cfs_rq **cfs_rq; /* 其他组调度运行队列成员省略 */ };一个 task_group 对应一个 cgroup 目录用户通过修改 cgroup 文件节点写入 quota/period/burst内核最终将参数落地保存至对应cfs_bandwidth结构体成员。1.3 关键限流术语throttle 节流分组周期内 CPU 耗时耗尽组内就绪任务停止调度加入阻塞等待队列unthrottle 解节流周期定时器到期刷新剩余可用配额被节流任务重新放回就绪队列参与调度runtime 剩余配额cfs_bandwidth 内部实时统计分组剩余可用 CPU 时间每消耗 CPU 时间则递减period_timer 周期定时器挂靠在 cfs_bandwidth 的内核定时器周期到达后重置剩余配额、释放节流任务。1.4 cfs_bandwidth 结构体核心定位cfs_bandwidth所有字段分为五大类周期配额配置字段、剩余运行时统计字段、定时器管理字段、节流任务队列字段、突发带宽统计字段所有 CFS 带宽限流逻辑的增减、判断、计时、排队全部围绕该结构体完成。二、环境准备2.1 软硬件环境清单环境项版本参数操作系统Ubuntu22.04 x86_64Linux 内核5.15 / 6.1 LTS主流发行版、容器生产环境通用内核编译依赖gcc 11.3、make、libncurses-dev、bison flex libelf-dev调试工具ftrace、perf、trace-cmd、crash (gdb 内核调试)cgroup 版本cgroup v2推荐现代容器默认兼容 v1说明5.13 及以上内核才完整支持 burst 突发带宽字段想要实操 burst 功能优先选用 6.1 内核。2.2 内核源码下载与编译配置1、安装编译依赖sudo apt update sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev git2、拉取 6.1 内核源码wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.tar.xz tar -xvf linux-6.1.tar.xz cd linux-6.13、开启必要内核配置项cp /boot/config-$(uname -r) .config make menuconfig开启以下编译选项必须勾选否则无 cgroup 带宽与调试能力CONFIG_CGROUP_SCHEDy # 开启CFS组调度 CONFIG_CGROUP_CPUACCTy # CPU资源统计 CONFIG_CFS_BANDWIDTHy # 核心启用CFS带宽控制器 CONFIG_FTRACEy # 函数跟踪调试 CONFIG_SCHED_DEBUGy # 调度调试开关 CONFIG_DEBUG_FSy # 调试文件系统 CONFIG_CGROUP_BPFn # 按需关闭不影响本次实验4、编译安装内核make -j$(nproc) sudo make modules_install sudo make install sudo update-grub重启服务器在 grub 菜单选择新编译内核启动。2.3 源码文件路径kernel/sched/fair.c // cfs_bandwidth结构体定义、全部操作函数实现 kernel/sched/sched.h // task_group结构体定义三、应用场景约 300 字cfs_bandwidth 驱动的 CFS 带宽限流是容器资源隔离的底层基石在云服务器、边缘工控、音视频集群三大场景落地广泛。K8s 集群中 Deployment 配置resources.limits.cpukubelet 最终转化为 cgroup cpu 配额写入内核参数落地存储在 cfs_bandwidth依靠结构体周期与配额字段限制容器 Pod 最大 CPU 占用避免单个业务进程突刺耗尽整机算力。工业嵌入式 Linux 设备多业务分区部署时设备采集、数据上报、人机交互三类进程分属不同 cgroup通过配置 cfs_bandwidth 的 quota 隔离 CPU防止采集任务满载抢占交互进程导致界面卡顿。流媒体转码服务器场景借助 cfs_bandwidth 的 burst 突发字段配置短时带宽扩容应对瞬间并发转码峰值既长期限制平均 CPU 上限又避免瞬时流量触发频繁节流任务休眠。四、实际案例与步骤、源码 实操代码4.1 cfs_bandwidth 完整结构体源码逐字段注释截取sched.h中结构体定义6.1 内核原版代码附工程化注释struct cfs_bandwidth { #ifdef CONFIG_CFS_BANDWIDTH /* 1. 基础配额配置用户从cgroup文件写入的原始参数 */ u64 quota; /* 用户配置单周期总配额(us)-1代表无限制 */ u64 period; /* 用户配置统计周期(us)默认100000us100ms */ u64 burst; /* 突发带宽配额空闲积攒富余CPU时间上限(us) */ /* 2. 运行时动态统计字段内核实时计算剩余可用时间 */ u64 runtime; /* 当前周期剩余可用CPU时间消耗则递减 */ u64 prev_runtime; /* 上一轮周期剩余runtime用于burst积攒计算 */ ktime_t period_start; /* 当前周期起始内核时间戳 */ /* 3. 定时器相关周期刷新、节流解挂依靠定时器 */ struct hrtimer period_timer; /* 高精度周期定时器周期到触发配额刷新 */ ktime_t next_period; /* 下一个周期到期时间点 */ /* 4. 节流队列被限流无法运行的调度实体挂载链表 */ struct list_head throttled_cfs_rq; /* 本分组下所有被节流的cfs运行队列链表 */ int throttled; /* 当前被节流的cfs_rq计数0代表分组处于限流状态 */ /* 5. 自旋锁多CPU并发修改结构体成员的保护锁 */ raw_spinlock_t lock; #endif };代码说明quota/period/burst 是静态配置字段用户态修改 cgroup 文件触发内核写函数赋值runtime/throttled/throttled_cfs_rq 是动态运行字段进程调度运行、CPU 耗时变化实时变更period_timer 是高精度定时器是周期重置、解除节流的触发源。4.2 内核关键操作函数源码fair.c4.2.1 cfs_bandwidth_period_reset周期到期重置配额周期定时器到期触发刷新 runtime、释放被节流任务核心逻辑static void cfs_bandwidth_period_reset(struct cfs_bandwidth *cfs_b, struct task_group *tg) { raw_spin_lock(cfs_b-lock); /* 周期重置重新填充可用配额叠加burst积攒余量 */ cfs_b-runtime cfs_b-quota; if (cfs_b-burst cfs_b-quota cfs_b-prev_runtime 0) { cfs_b-runtime cfs_b-prev_runtime; /* 不能超过burst上限 */ if (cfs_b-runtime cfs_b-burst) cfs_b-runtime cfs_b-burst; } cfs_b-prev_runtime 0; /* 解除全部被节流的运行队列 */ if (cfs_b-throttled 0) { unthrottle_cfs_rq_list(cfs_b, tg); } /* 更新周期起始时间戳 */ cfs_b-period_start ktime_get(); raw_spin_unlock(cfs_b-lock); }功能每次 period 周期走完刷新剩余可用 CPU 时间若有积攒的 burst 余量追加 runtime唤醒之前因超配额被阻塞的进程。4.2.2 __refill_cfs_bandwidth_runtime进程消耗 CPU 扣减 runtime分组内进程运行消耗 CPU 时内核实时扣减 cfs_b-runtime耗尽触发节流static int __refill_cfs_bandwidth_runtime(struct cfs_rq *cfs_rq, u64 delta_exec) { struct task_group *tg cfs_rq-tg; struct cfs_bandwidth *cfs_b tg-cfs_b; raw_spin_lock(cfs_b-lock); /* 无配额限制直接返回不做限流 */ if (cfs_b-quota RUNTIME_INF) { raw_spin_unlock(cfs_b-lock); return 0; } /* 消耗CPU时间剩余配额递减 */ if (cfs_b-runtime delta_exec) { cfs_b-runtime - delta_exec; raw_spin_unlock(cfs_b-lock); return 0; } /* runtime耗尽触发节流当前cfs_rq加入阻塞链表 */ throttle_cfs_rq(cfs_rq, cfs_b); raw_spin_unlock(cfs_b-lock); return 1; }逻辑剩余 runtime 不够本次进程 CPU 消耗执行 throttle_cfs_rq将当前分组运行队列加入 throttled_cfs_rq 链表分组内任务暂停调度。4.3 用户态实操cgroup v2 配置 CPU 带宽修改 cfs_bandwidth 参数步骤 1挂载 cgroup2 文件系统# 创建挂载目录 sudo mkdir -p /sys/fs/cgroup2 # 挂载cgroup v2 sudo mount -t cgroup2 none /sys/fs/cgroup2 # 创建测试分组test_cfs_bw sudo mkdir /sys/fs/cgroup2/test_cfs_bw步骤 2配置带宽配额等效修改内核 cfs_bandwidth-quota/period# period100000us(默认100ms)quota20000us(20%CPU上限) echo 20000 100000 | sudo tee /sys/fs/cgroup2/test_cfs_bw/cpu.max # 配置burst突发上限50000us echo 50000 | sudo tee /sys/fs/cgroup2/test_cfs_bw/cpu.burstcpu.max 格式quota period写入后内核自动赋值到对应 task_group.cfs_bandwidth 的 quota、period 成员。步骤 3编写 CPU 压测程序消耗 CPU 触发 CFS 节流// cfs_stress.c 死循环消耗CPU用于触发带宽限流 #include stdio.h int main(void) { unsigned long cnt 0; while(1){ cnt; } return 0; }编译运行gcc cfs_stress.c -o cfs_stress # 后台启动压测进程 ./cfs_stress # 获取进程PID PID$! # 将进程加入cgroup分组 echo $PID | sudo tee /sys/fs/cgroup2/test_cfs_bw/cgroup.procs步骤 4ftrace 跟踪 cfs_bandwidth 相关内核函数观测节流触发# 挂载debugfs sudo mount -t debugfs none /sys/kernel/debug # 清空跟踪缓存 sudo echo /sys/kernel/debug/tracing/trace # 设置需要跟踪的带宽关键函数 sudo echo throttle_cfs_rq /sys/kernel/debug/tracing/set_ftrace_filter sudo echo unthrottle_cfs_rq_list /sys/kernel/debug/tracing/set_ftrace_filter sudo echo cfs_bandwidth_period_reset /sys/kernel/debug/tracing/set_ftrace_filter # 开启函数跟踪 sudo echo function /sys/kernel/debug/tracing/current_tracer sudo echo 1 /sys/kernel/debug/tracing/tracing_on # 等待30秒后关闭跟踪 sleep 30 sudo echo 0 /sys/kernel/debug/tracing/tracing_on # 查看跟踪日志能看到周期性throttle与unthrottle调用 cat /sys/kernel/debug/tracing/trace现象说明压测进程满载消耗 CPUruntime 快速耗尽触发throttle_cfs_rq每 100ms 周期定时器到期执行cfs_bandwidth_period_reset执行unthrottle_cfs_rq_list释放任务进程周期性启停CPU 稳定卡在 20% 左右。4.4 利用 crash 调试工具查看运行时 cfs_bandwidth 内存数据# 安装crash工具 sudo apt install crash # 获取内核符号 sudo apt install linux-image-6.1-dbg # 启动crash关联内核 sudo crash /usr/lib/debug/vmlinux-6.1 /dev/memcrash 交互终端执行# 查找test_cfs_bw对应的task_group结构体 search task_group # 查看cfs_bandwidth成员数值实时查看quota/runtime/throttled字段 struct cfs_bandwidth 0xxxxxxx可以直观看到运行中 runtime 持续下降、throttled 在 0/1 之间切换验证结构体动态变化逻辑。五、常见问题与解答Q1配置 cpu.max-1 100000 之后容器仍然被限流cfs_bandwidth 不生效答quota-1 在内核中定义为 RUNTIME_INF 无限配额若依旧限流优先核查1、内核 CONFIG_CFS_BANDWIDTH 配置是否开启2、父 cgroup 存在上层 CPU 限额子分组继承父带宽限制父 cfs_bandwidth 配额耗尽连带子分组节流3、系统存在 cpuset 绑定 CPU 数量少于业务进程数不是 CFS 带宽问题。通过 ftrace 跟踪throttle_cfs_rq判断节流来源分组。Q2配置 burst 突发带宽不生效cfs_b-burst 字段写入无效答burst 从 Linux5.13 版本正式并入主线低于该版本内核无此字段其次 burst 生效前提分组周期内空闲prev_runtime 积攒富余时间全程满载跑 CPU 无空闲时段无法积攒余量看不到突发扩容效果。调低 quota间歇启停压测程序即可复现 burst。Q3修改 cgroup cpu.max 参数后cfs_bandwidth-quota 没有实时更新答用户态写入 cpu.max 文件触发内核cpu_max_write函数内核加 cfs_b-lock 自旋锁后赋值若进程正在疯狂消耗 CPU 占用锁少量场景会延迟刷新可通过重新迁移进程出再入 cgroup强制触发参数重载。Q4perf 观测进程 CPU 使用率忽高忽低周期性抖动是 cfs_bandwidth 节流导致答大概率。配额过小runtime 快速耗尽进入 throttle 休眠周期到解节流瞬间 CPU 拉满形成锯齿曲线。解决方案适度调高 quota 或开启 burst 字段平滑瞬时峰值优化 cfs 带宽配置。六、实践建议与最佳实践容器生产配置规范K8s 配置 limits.cpu 时尽量避免极小配额小于 10ms/100ms 周期过小 quota 极易频繁触发 throttle业务周期性卡顿常规业务推荐 period 保持默认 100000usquota 按需配置峰值波动大的业务开启 burst。内核调试排障技巧排查 CPU 限额异常优先三步走①ftrace 跟踪 throttle_cfs_rq 确认是否触发 CFS 节流②crash 查看对应 task_group 的 cfs_bandwidth-runtime、throttled 字段实时值③逐层向上遍历父 cgroup排查上层分组带宽限制。嵌入式设备调优工控嵌入式产品固定硬件 CPU 核心使用 cpuset 绑定 cgroup 到指定核避免多核心调度导致 cfs_bandwidth 跨核统计错乱减少自旋锁竞争带来的微小调度开销。内核二次开发优化建议自研自定义带宽管控不要直接修改原生 cfs_bandwidth 结构体可基于现有字段扩展私有结构体挂载 task_group复用原生 period_timer 定时器与 throttle 队列逻辑规避原生调度锁与内核同步问题。线上故障应急方案业务因意外节流卡死时临时修改 cpu.max-1 解除配额限制快速恢复业务后续再优化配额与 burst 参数。七、总结与应用延伸全文围绕cfs_bandwidth结构体从字段定义、内核源码实现、用户态 cgroup 实操、调试排查完整梳理 CFS 带宽限流底层原理该结构体是 CFS 组调度 CPU 资源硬隔离的核心载体静态配置类字段quota/period/burst承接用户层 cgroup 配置动态统计字段runtime/throttled跟随进程调度实时变更定时器与链表字段实现周期刷新、任务节流排队整套闭环。从落地场景来看cfs_bandwidth 支撑了 Docker/K8s 容器资源限额、云主机租户资源隔离、嵌入式多业务分区资源管控三大主流方向从技术研究层面该结构体设计思路是操作系统资源限流经典实现可用于调度相关毕业论文撰写、内核资源隔离模块二次开发。