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

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 子系统:

  1. quota(配额):一个周期内,该 cgroup 分组全部进程总共能使用的 CPU 总时间(单位微秒 us);
  2. period(周期):带宽统计的统计窗口时长(单位微秒 us),默认 100ms 即 100000us;

例:quota=20000,period=100000,代表该分组在每 100ms 周期内最多占用单个 CPU 20ms 时间,等效限制 CPU 使用率上限 20%。

  1. 突发带宽(burst):Linux 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_64
Linux 内核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 git
2、拉取 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.1
3、开启必要内核配置项
cp /boot/config-$(uname -r) .config make menuconfig

开启以下编译选项(必须勾选,否则无 cgroup 带宽与调试能力):

CONFIG_CGROUP_SCHED=y # 开启CFS组调度 CONFIG_CGROUP_CPUACCT=y # CPU资源统计 CONFIG_CFS_BANDWIDTH=y # 核心:启用CFS带宽控制器 CONFIG_FTRACE=y # 函数跟踪调试 CONFIG_SCHED_DEBUG=y # 调度调试开关 CONFIG_DEBUG_FS=y # 调试文件系统 CONFIG_CGROUP_BPF=n # 按需关闭,不影响本次实验
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.cpu,kubelet 最终转化为 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),默认100000us=100ms */ 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 };

代码说明

  1. quota/period/burst 是静态配置字段,用户态修改 cgroup 文件触发内核写函数赋值;
  2. runtime/throttled/throttled_cfs_rq 是动态运行字段,进程调度运行、CPU 耗时变化实时变更;
  3. period_timer 是高精度定时器,是周期重置、解除节流的触发源。

4.2 内核关键操作函数源码(fair.c)

4.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)
# period=100000us(默认100ms),quota=20000us(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.burst

cpu.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
步骤 4:ftrace 跟踪 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

现象说明:压测进程满载消耗 CPU,runtime 快速耗尽,触发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/mem

crash 交互终端执行:

# 查找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,强制触发参数重载。

Q4:perf 观测进程 CPU 使用率忽高忽低周期性抖动,是 cfs_bandwidth 节流导致?

:大概率。配额过小,runtime 快速耗尽进入 throttle 休眠,周期到解节流瞬间 CPU 拉满,形成锯齿曲线。解决方案:适度调高 quota 或开启 burst 字段平滑瞬时峰值,优化 cfs 带宽配置。

六、实践建议与最佳实践

  1. 容器生产配置规范K8s 配置 limits.cpu 时尽量避免极小配额(小于 10ms/100ms 周期),过小 quota 极易频繁触发 throttle,业务周期性卡顿;常规业务推荐 period 保持默认 100000us,quota 按需配置,峰值波动大的业务开启 burst。

  2. 内核调试排障技巧排查 CPU 限额异常优先三步走:①ftrace 跟踪 throttle_cfs_rq 确认是否触发 CFS 节流;②crash 查看对应 task_group 的 cfs_bandwidth->runtime、throttled 字段实时值;③逐层向上遍历父 cgroup,排查上层分组带宽限制。

  3. 嵌入式设备调优工控嵌入式产品固定硬件 CPU 核心,使用 cpuset 绑定 cgroup 到指定核,避免多核心调度导致 cfs_bandwidth 跨核统计错乱,减少自旋锁竞争带来的微小调度开销。

  4. 内核二次开发优化建议自研自定义带宽管控不要直接修改原生 cfs_bandwidth 结构体,可基于现有字段扩展私有结构体挂载 task_group,复用原生 period_timer 定时器与 throttle 队列逻辑,规避原生调度锁与内核同步问题。

  5. 线上故障应急方案业务因意外节流卡死时,临时修改 cpu.max=-1 解除配额限制,快速恢复业务,后续再优化配额与 burst 参数。

七、总结与应用延伸

全文围绕cfs_bandwidth结构体从字段定义、内核源码实现、用户态 cgroup 实操、调试排查完整梳理 CFS 带宽限流底层原理,该结构体是 CFS 组调度 CPU 资源硬隔离的核心载体:静态配置类字段(quota/period/burst)承接用户层 cgroup 配置,动态统计字段(runtime/throttled)跟随进程调度实时变更,定时器与链表字段实现周期刷新、任务节流排队整套闭环。

从落地场景来看,cfs_bandwidth 支撑了 Docker/K8s 容器资源限额、云主机租户资源隔离、嵌入式多业务分区资源管控三大主流方向;从技术研究层面,该结构体设计思路是操作系统资源限流经典实现,可用于调度相关毕业论文撰写、内核资源隔离模块二次开发。

http://www.jsqmd.com/news/938937/

相关文章:

  • 【Redis从入门到精通】第38篇:serverCron——Redis的“心跳“定时任务干了哪些活
  • 湘潭CMA甲醛检测治理公司深度测评:绿居净环保稳居榜首 - 五金回收
  • AI | ollama - [入门]
  • 旧项目安装QtFusion找不到IMcore:补wheel依赖还是迁移VibeFlux
  • 告别模板化论文困局:okbiye 分层式毕设创作体系,从资料上传到终稿排版全链路落地
  • 基于LM324运放的土壤湿度监测电路设计与实践
  • BetterGI AI自动化游戏辅助工具完整教程:从新手到专家的原神自动化指南
  • STM32F103硬件SPI直驱GC9A01芯片1.28寸240×240 TFT屏,含GUI与测试例程
  • 基于Arduino与HC-SR04的超声波表情显示系统设计与实现
  • 如何轻松地将 iTunes 备份传输到三星
  • 宠物帮扶信息平台宠物领养寻宠登记Java整套源码部署
  • Linux内核启动探秘:Ramdisk从解压到执行init的完整流程解析
  • 2026年硅灰厂家选型指南:微硅粉多少钱一吨、微硅粉市场价格、微硅粉生产厂家、硅灰价格、硅灰多少钱一吨、硅灰粉生产厂家选择指南 - 优质品牌商家
  • 湘潭母婴除甲醛CMA甲醛检测治理公司2026深度测评:森氧家环保稳居榜首 - 五金回收
  • 英伟达Vera Rubin芯片:Blackwell直接过时?Agentic AI时代的硬件革命
  • 7个技巧:让你的普通鼠标在Mac上超越苹果触控板
  • 从开题立项逻辑拆解到文稿落地:深度解析 okbiye AI 开题报告模块的学术工程化设计与实战价值
  • 谷歌云的这套“真相探测仪“彻底揭穿了它们的把戏
  • 基于Arduino的智能烟雾报警器DIY:从传感器原理到嵌入式系统实战
  • SpringBoot开发宠物帮扶系统领养认领信息管理源码详解
  • 智能优化算法论文适合投哪些期刊?遗传算法、粒子群、灰狼算法、鲸鱼算法投稿方向分析
  • 芜湖母婴除甲醛CMA甲醛检测治理公司深度测评:清醛卫士稳居榜首 - 五金回收
  • 通化母婴除甲醛CMA甲醛检测治理公司2026深度测评:森氧家环保稳居榜首 - 五金回收
  • 梧州CMA甲醛检测治理公司深度测评:绿居净环保稳居榜首 - 五金回收
  • 通化母婴除甲醛CMA甲醛检测治理公司深度测评:清醛卫士稳居榜首 - 五金回收
  • 基于Arduino与MPU-6050的体感游戏手套DIY全攻略
  • 赛博朋克2077存档编辑器:解锁夜之城的无限可能
  • 基于树莓派的智能叠衣机器人:从传感器到伺服电机的闭环系统实践
  • AI 视频生成进入工作流阶段:Runway Agent、Aleph 2.0、Adobe Gemini 连接器盘点
  • 如何用WeChatMsg颠覆你的数字记忆管理:3步打造个人AI数据银行