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

Linux CFS 带宽控制:cfs_quota_us 与 cfs_period_us 的资源限制

简介

在 Linux 系统中,完全公平调度器(CFS)是面向普通分时任务的核心调度模块,依靠cpu.shares实现 CPU 资源的比例分配,但这种分配方式存在明显短板:它只能划分任务组之间的资源权重,无法对单个任务组设置硬上限。在服务器集群、云容器、嵌入式设备、多业务混部服务器等场景下,一旦某个业务进程出现死循环、内存泄漏、逻辑异常等问题,会持续抢占 CPU 资源,挤压其他正常业务的运行空间,最终引发整机负载过高、业务响应延迟、系统卡顿甚至宕机。

为解决这一问题,Linux 内核基于 Cgroup v1 的 CPU 子系统,扩展出CFS 带宽控制(CFS Bandwidth Control)功能,通过cpu.cfs_quota_us(CPU 配额)与cpu.cfs_period_us(调度周期)两个核心参数,为每一个任务组(cgroup)配置固定的 CPU 使用上限,实现 CPU 资源的硬限制。该机制不改变 CFS 本身的公平调度逻辑,而是在调度流程中增加带宽节流环节:当任务组在一个调度周期内耗尽配额时间后,组内所有 CFS 任务会被暂时节流挂起,直到下一个周期重置配额后才能继续运行。

目前这套带宽控制机制是 Docker、K8s 等容器平台 CPU 限流的底层实现,也是运维工程师、内核开发人员、云原生架构师、嵌入式 Linux 开发者必须掌握的核心技术。无论是线上故障排查、容器资源配额规划、混部服务器稳定性优化,还是内核源码研究、学术论文撰写,深入理解cfs_quota_uscfs_period_us的工作原理、内核实现、实操配置与异常处理,都具备极高的工程价值。本文结合内核源码、实操命令、测试案例与排错经验,从原理、环境、实战、问题排查到最佳实践做完整拆解,内容可直接用于项目落地与技术文档编写。

一、核心概念与术语解析

1.1 CFS 调度与 Cgroup 组调度基础

CFS(Completely Fair Scheduler,完全公平调度器)是 Linux 2.6.23 版本之后默认的普通进程调度器,核心思想是按照任务权重均分 CPU 时间,保证所有就绪任务获得相对公平的运行时长。

Cgroup(控制组)是 Linux 内核提供的资源隔离与限制技术,CONFIG_FAIR_GROUP_SCHED开启后,CFS 支持组调度:将多个进程 / 线程划入同一个任务组,以组为单位进行 CPU 调度、资源统计与限制。CFS 带宽控制是组调度的扩展功能,依赖内核配置CONFIG_CFS_BANDWIDTH

1.2 核心参数定义(单位:微秒 us)

1.2.1 cpu.cfs_period_us

代表调度周期,定义带宽统计的时间窗口。

  • 取值范围:内核规范为1000 ~ 1000000(1ms ~ 1s),超出范围会写入失败;
  • 默认值:绝大多数发行版默认100000(100ms);
  • 作用:所有配额统计、带宽消耗、节流判断,都以该周期为单位循环执行,周期结束后自动重置组内已使用 CPU 时长。
1.2.2 cpu.cfs_quota_us

代表CPU 时间配额,即当前任务组在一个cfs_period_us周期内,最多能使用的 CPU 总时长。

  • 取值规则:支持正数、负数;
  • 默认值:-1,表示无配额限制,任务组可以耗尽整机 CPU 资源;
  • 计算规则:CPU 最大使用率 = cfs_quota_us /cfs_period_us × 100%
  • 特殊说明:配额可以大于周期值,例如周期 100ms、配额 200ms,代表该组可以独占2 个 CPU 核心的算力。

1.3 节流(Throttle)与解除节流

  • 节流:当任务组在当前周期内消耗的 CPU 时长达到cfs_quota_us配额时,内核将组内所有 CFS 任务标记为节流状态,不再参与 CPU 调度,任务被挂起。
  • 解除节流:当前cfs_period_us周期结束,内核自动重置该组的已消耗 CPU 时长,节流状态清除,组内任务恢复正常调度。

1.4 关键内核数据结构

CFS 带宽控制的核心统计与控制结构定义在kernel/sched/fair.c中,cfs_rq是每个 CPU 上的 CFS 运行队列,扩展了带宽相关成员:

// kernel/sched/fair.c 节选,CFS运行队列带宽控制相关字段 struct cfs_rq { /* 基础CFS调度成员,省略 */ struct sched_entity *curr; struct rb_root_cached tasks_timeline; #ifdef CONFIG_CFS_BANDWIDTH /* 带宽控制开关:1=开启配额限制,0=关闭 */ int runtime_enabled; /* 当前周期内已使用的CPU时长 */ u64 runtime_used; /* 周期定时器,用于周期重置、解除节流 */ struct timer_list cfs_bandwidth_timer; /* 节流计数与状态标记 */ unsigned int throttled; /* 任务组带宽配置:配额、周期 */ struct cfs_bandwidth *tg_cfs_bw; #endif };

throttled字段是排查限流问题的核心,数值大于 0 代表当前队列存在被节流的任务。

1.5 相关工具说明

  • mount:查看 debugfs、cgroup 文件系统挂载状态;
  • top / htop:观测进程 CPU 使用率,验证限流效果;
  • perf:跟踪内核节流函数、统计调度耗时;
  • ftrace:动态跟踪 CFS 带宽控制内核函数调用流程;
  • cat/echo:原生文件接口,配置与读取 cgroup 配额参数。

二、环境准备

2.1 软硬件环境清单

分类版本 / 配置要求说明
操作系统Ubuntu 20.04 / 22.04、CentOS 7/8/9主流服务器发行版,均默认开启 Cgroup v1
内核版本Linux 5.4、5.15、6.1 LTS全系列支持 CFS 带宽控制,源码逻辑一致
硬件x86_64 架构,2 核 4G 及以上多核环境可验证配额大于周期的场景
依赖工具gcc、make、libncurses-dev、bison、flex内核编译、源码调试使用
调试工具perf、trace-cmd、gdb、htop观测限流状态、跟踪内核调用

2.2 环境检查与基础配置

2.2.1 检查 Cgroup 文件系统挂载

现代 Linux 发行版默认已挂载 cgroup,执行以下命令验证:

# 查看cgroup挂载点 mount | grep cgroup

正常输出会包含/sys/fs/cgroup/cpu,代表 CPU 子系统已就绪。如果未挂载,手动执行挂载命令:

# 创建挂载目录并挂载cgroup cpu子系统 sudo mkdir -p /sys/fs/cgroup/cpu sudo mount -t cgroup -o cpu none /sys/fs/cgroup/cpu
2.2.2 检查内核配置项

必须确保内核开启以下两个核心配置,否则带宽控制功能失效:

# 查看是否开启组调度与CFS带宽控制 zcat /proc/config.gz | grep -E "FAIR_GROUP_SCHED|CFS_BANDWIDTH"

正常结果:

CONFIG_FAIR_GROUP_SCHED=y CONFIG_CFS_BANDWIDTH=y

若为=m=n,需要重新编译内核开启对应选项。

2.2.3 安装辅助工具
# Ubuntu/Debian 系列 sudo apt update && sudo apt install htop perf trace-cmd -y # CentOS/RHEL 系列 sudo yum install htop perf -y

2.3 源码路径说明

后续源码阅读与分析均基于以下路径:

  1. CFS 带宽控制核心逻辑:kernel/sched/fair.c
  2. Cgroup CPU 子系统文件接口:kernel/sched/cpuacct.ckernel/cgroup/cgroup.c
  3. 官方文档参考:Documentation/scheduler/sched-bwc.rst

三、应用场景

CFS 带宽控制是线上服务稳定性保障的核心手段,落地场景十分广泛。在云服务器与容器集群中,K8s、Docker 依靠该机制限制单个容器 CPU 配额,避免容器之间相互抢占资源,实现租户资源隔离。在物理机多业务混部场景下,将日志采集、监控告警、定时任务等低优先级业务划入独立 cgroup,限制其 CPU 使用率,保障核心交易、接口服务等高优先级业务稳定运行。嵌入式 Linux 设备中,后台守护进程、日志服务会被设置 CPU 上限,防止后台进程耗尽算力导致前台交互卡顿。此外,压力测试、性能压测场景中,通过配额限制压测进程的 CPU 占用,精准控制压测强度;运维故障应急时,可快速限制异常高负载进程的 CPU 资源,临时止损。整套机制从云原生、服务器到嵌入式终端,全面覆盖资源隔离与限流需求。

四、实际案例与步骤(含命令、代码、源码解析)

本章从基础配置、压测验证、内核源码解析、自定义测试程序、函数跟踪五个维度逐步实操,所有命令与代码均可直接复制运行。

4.1 基础操作:创建 Cgroup 并配置 CPU 配额

步骤 1:新建自定义任务组

在 cgroup cpu 目录下创建目录,即生成一个独立任务组:

# 进入cpu子系统根目录 cd /sys/fs/cgroup/cpu # 创建名为 cpu_limit_demo 的任务组 sudo mkdir cpu_limit_demo cd cpu_limit_demo
步骤 2:查看默认参数
# 查看默认周期与配额 cat cpu.cfs_period_us cat cpu.cfs_quota_us

输出说明

  • cpu.cfs_period_us默认100000(100ms);
  • cpu.cfs_quota_us默认-1(无限制)。
步骤 3:配置配额与周期(限制 CPU 使用率)

案例 1:限制为单核 CPU 的 20%计算公式:100ms周期 × 20% = 20ms配额

# 设置周期为默认100000us(100ms) sudo echo 100000 > cpu.cfs_period_us # 设置配额为20000us(20ms) sudo echo 20000 > cpu.cfs_quota_us

此时该组内所有进程最大 CPU 使用率被限制在 20%

案例 2:限制使用 2 个完整 CPU 核心周期 100ms,配额设置为 200ms(200000us):

sudo echo 200000 > cpu.cfs_quota_us sudo echo 100000 > cpu.cfs_period_us

案例 3:关闭配额限制恢复默认无限制状态:

sudo echo -1 > cpu.cfs_quota_us

4.2 实战验证:压测进程 + 观测限流效果

步骤 1:编写 CPU 压测程序(无限消耗 CPU)

创建cpu_stress.c,模拟死循环高负载进程:

#include <stdio.h> /* 死循环消耗CPU,纯计算任务 */ int main(void) { unsigned long long i = 0; while (1) { i++; i--; } return 0; }

代码说明:无 IO、无休眠,持续占用 CPU,用于验证限流是否生效。

编译运行:

gcc cpu_stress.c -o cpu_stress # 后台运行压测程序 ./cpu_stress & # 记录进程PID echo $! > stress_pid.txt
步骤 2:将进程加入限流 Cgroup

把压测进程 PID 写入tasks文件,进程即被纳入当前任务组管控:

# 读取PID并加入cgroup sudo cat stress_pid.txt | sudo tee tasks
步骤 3:使用 htop/top 观测 CPU 使用率

打开新终端执行:

htop

现象验证: 配置20000/100000配额后,压测进程 CPU 使用率稳定在20% 左右,无法打满单核 CPU;修改为-1无限制后,进程会立即打满单核 CPU。

步骤 4:查看节流状态

cgroup 提供cpu.stat文件,统计带宽节流信息:

cat cpu.stat

关键字段解释:

nr_periods : 已经经过的调度周期总数 nr_throttled : 触发节流的周期数 throttled_time : 累计被节流挂起的总时长(纳秒)

nr_throttled持续增长,代表配额耗尽、限流生效。

4.3 内核核心源码解析(CFS 带宽控制核心逻辑)

4.3.1 配额写入接口函数

当我们通过echo修改cfs_quota_uscfs_period_us时,最终会调用内核写入函数,文件接口逻辑如下(kernel/sched/fair.c):

/* 写入 cfs_quota_us 回调函数 */ static ssize_t cpu_cfs_quota_write_s64(struct kernfs_file *kf, char __user *buf, size_t count, loff_t *ppos) { struct task_group *tg = kf->priv; s64 quota; // 从用户态读取写入的配额值 if (kstrtos64_from_user(buf, count, 10, &quota)) return -EINVAL; // 调用核心接口,更新任务组带宽配置 tg_set_cfs_bandwidth(tg, quota, tg->cfs_bandwidth.period); return count; } /* 写入 cfs_period_us 回调函数 */ static ssize_t cpu_cfs_period_write_u64(struct kernfs_file *kf, char __user *buf, size_t count, loff_t *ppos) { struct task_group *tg = kf->priv; u64 period; if (kstrtou64_from_user(buf, count, 10, &period)) return -EINVAL; // 校验周期范围:1ms ~ 1s if (period < 1000 || period > 1000000) return -EINVAL; tg_set_cfs_bandwidth(tg, tg->cfs_bandwidth.quota, period); return count; }

代码解读

  1. 两个文件写入接口最终都会调用tg_set_cfs_bandwidth统一更新带宽配置;
  2. 内核强制校验cfs_period_us范围,非法数值直接返回写入失败;
  3. 配置更新后,内核会重新计算各 CPU 队列的可用配额。
4.3.2 配额消耗与节流判断逻辑

CFS 每次调度任务运行时,都会累加runtime_used(已使用时长),并做节流判断:

// 调度切片结束后,统计CPU耗时并判断是否需要节流 static void cfs_bandwidth_account(struct cfs_rq *cfs_rq, u64 delta_exec) { struct cfs_bandwidth *bw = cfs_rq->tg_cfs_bw; // 无配额限制,直接返回 if (bw->quota == -1) return; // 累加当前周期已使用CPU时长 cfs_rq->runtime_used += delta_exec; // 判断:已使用时长 >= 配额 → 触发节流 if (cfs_rq->runtime_used >= bw->quota) { cfs_rq->throttled++; // 标记队列节流,禁止新任务调度 cfs_rq->runtime_enabled = 0; // 启动周期定时器,到期后解除节流 cfs_bandwidth_start_timer(cfs_rq); } }

核心流程

  1. 任务每运行一个时间片,就累加消耗时长;
  2. 达到配额上限后,关闭队列调度能力,标记为节流;
  3. 启动周期定时器,等待当前period结束。
4.3.3 周期定时器:重置配额、解除节流

周期到期后,定时器回调函数执行重置逻辑:

static void cfs_bandwidth_timer(struct timer_list *timer) { struct cfs_rq *cfs_rq = timer_container_of(timer, struct cfs_rq, cfs_bandwidth_timer); struct cfs_bandwidth *bw = cfs_rq->tg_cfs_bw; // 重置当前周期已使用时长 cfs_rq->runtime_used = 0; // 解除节流,恢复调度 cfs_rq->runtime_enabled = 1; // 唤醒队列中被节流的任务 check_cfs_rq_runtime(cfs_rq); }

这是带宽控制循环执行的核心:计时到期 → 清零消耗 → 解除限流 → 重新开始统计

4.4 Ftrace 跟踪内核调用流程

使用 ftrace 动态跟踪带宽控制相关函数,直观验证源码逻辑:

# 挂载debugfs(大部分系统已默认挂载) sudo mount -t debugfs none /sys/kernel/debug # 清空跟踪缓存 sudo echo > /sys/kernel/debug/tracing/trace # 设置需要跟踪的内核函数 sudo echo cfs_bandwidth_account >> /sys/kernel/debug/tracing/set_ftrace_filter sudo echo cfs_bandwidth_timer >> /sys/kernel/debug/tracing/set_ftrace_filter sudo echo tg_set_cfs_bandwidth >> /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

此时保持压测程序运行,等待 10 秒后停止跟踪并查看日志:

# 关闭跟踪 sudo echo 0 > /sys/kernel/debug/tracing/tracing_on # 查看调用日志 sudo cat /sys/kernel/debug/tracing/trace

日志解读:可以看到cfs_bandwidth_account持续被调用(统计 CPU 消耗),配额耗尽后触发定时器cfs_bandwidth_timer,完成周期重置。

4.5 退出 Cgroup 与清理环境

实操完成后,恢复环境:

# 终止压测进程 sudo kill $(cat stress_pid.txt) # 将进程移出cgroup(写入根cgroup的tasks) sudo echo $$ | sudo tee /sys/fs/cgroup/cpu/tasks # 删除自定义cgroup目录 sudo rmdir /sys/fs/cgroup/cpu/cpu_limit_demo

五、常见问题与解答

Q1:修改 cfs_period_us 写入失败,提示权限错误或参数非法?

解答:第一检查数值范围,cfs_period_us必须在1000~1000000(1ms~1s)之间,超出直接写入失败;第二必须使用root/sudo权限操作 cgroup 文件,普通用户无写入权限;第三检查内核是否开启CONFIG_CFS_BANDWIDTH

Q2:设置了配额,但进程 CPU 使用率依然打满,限流不生效?

解答:1. 检查cfs_quota_us是否误写为-1(无限制);2. 确认进程 PID 已经正确写入当前 cgroup 的tasks文件;3. 区分 CFS 任务与实时任务:SCHED_FIFO/SCHED_RR实时任务不受 CFS 带宽控制,需要使用 RT 带宽限制;4. 多核场景下,配额大于周期代表允许多核运行,属于正常现象。

Q3:cpu.stat 中 nr_throttled 不断增长,业务响应变慢如何处理?

解答nr_throttled上涨代表进程持续耗尽配额被节流。若为正常业务,说明配额设置过小,需要调高cfs_quota_us;若为异常进程,先排查代码死循环、逻辑 BUG,再临时收紧配额止损。

Q4:父子 Cgroup 的配额是否会叠加生效?

解答:Cgroup 是层级结构,子组配额不能超过父组配额。如果父组限制 50% CPU,子组即使设置 100% 配额,最大也只能使用 50%,资源限制遵循层级约束。

Q5:为什么不建议把 cfs_period_us 设置为 1s(最大值)?

解答:周期越大,带宽统计窗口越长,CPU 占用的抖动会越明显。1s 周期下,进程会先打满 CPU 长达 1s,随后被节流挂起,出现 “突高突低” 的负载波形。常规业务建议使用默认 100ms,兼顾限流精度与系统平滑性。

Q6:Docker/K8s 中的 --cpus 参数和这两个字段是什么关系?

解答:Docker 的--cpus 0.5本质就是封装了cfs_quota_us/cfs_period_us,默认周期 100ms,0.5 核对应quota=50000,底层实现完全一致。

六、实践建议与最佳实践

6.1 配额与周期选型规范

  1. 常规在线业务:保持cfs_period_us=100000(默认 100ms),仅调整配额即可,调度抖动最小,是生产环境首选。
  2. 高抖动容忍业务(离线计算、日志处理):可适当放大周期至 200ms~500ms,减少定时器触发次数,降低内核开销。
  3. 低延迟业务(接口、交易服务):不建议修改默认周期,避免长时间节流导致请求超时。

6.2 生产环境资源划分最佳实践

  1. 核心业务独立分组:将数据库、接口服务、核心业务进程划入独立 cgroup,分配充足配额,优先保障运行。
  2. 边缘业务严格限流:日志采集、备份、监控、定时任务统一分组,设置低配额,避免抢占核心资源。
  3. 禁止全局使用 - 1 无限制:混部服务器中,除内核进程外,所有业务组都建议配置合理上限,作为故障兜底。

6.3 故障排查流程(CPU 限流异常)

  1. 第一步:查看cpu.stat,确认nr_throttled是否增长,判断是否触发节流;
  2. 第二步:核对cfs_quota_uscfs_period_us配置,计算理论最大使用率;
  3. 第三步:检查进程是否正确加入对应 cgroup;
  4. 第四步:区分 CFS 普通任务与 RT 实时任务,排除实时任务不受限问题;
  5. 第五步:使用perf/ftrace跟踪内核函数,定位内核层异常。

6.4 性能优化技巧

  1. 不要频繁动态修改配额与周期,每次修改都会调用tg_set_cfs_bandwidth重构带宽配置,产生微小性能开销。
  2. 高并发场景下,单个 cgroup 内不要承载过多进程,进程拆分到多个子 cgroup,提升调度效率。
  3. 嵌入式设备中,可适当缩小周期(不低于 1ms),提升限流的实时性。

6.5 内核二次开发建议

若需要定制带宽控制逻辑,建议基于原有cfs_bandwidth框架扩展,不要重构定时器与统计逻辑;新增限流规则时,复用runtime_usedthrottled字段,保证和原生调度逻辑兼容。

七、总结与应用延伸

本文完整讲解了 Linux CFS 带宽控制的核心原理、cfs_quota_uscfs_period_us的参数规则、内核源码实现、端到端实操案例、问题排查与工程最佳实践。简单总结核心要点:

  1. 两个参数组合实现 CPU 硬限流,使用率 = 配额 / 周期,周期合法范围 1ms~1s;
  2. 内核通过runtime_used统计 CPU 耗时,耗尽配额后触发节流,周期结束自动重置;
  3. 该机制仅作用于 CFS 普通任务,Linux 实时调度任务不受此限制;
  4. 容器技术、服务器混部、嵌入式系统是该功能最主要的落地场景。

CFS 带宽控制是 Linux 资源管控体系中承上启下的关键模块,上接 CFS 调度核心,下接 Cgroup 资源隔离,也是云原生、运维、内核开发的必备知识点。对于研发人员来说,不仅要会配置参数,更要理解内核节流、定时器、统计计数的底层逻辑,才能在业务卡顿、CPU 限流、容器资源异常等问题中快速定位根因。

建议读者基于本文的压测代码、ftrace 命令、内核源码片段,在测试机上反复复现实验,尝试修改不同配额与周期观察负载变化,结合线上业务场景规划合理的资源配额策略,将理论知识落地到实际项目中。同时可以延伸学习 Cgroup v2、RT 实时带宽控制,形成完整的 Linux CPU 资源管控知识体系。

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

相关文章:

  • 2026年10款降AIGC平台横评:最高AI率100%直降至0.12% - 降AI小能手
  • 如何高效编辑Unity游戏资源:跨平台逆向工程终极指南
  • 【Linux】线程同步和互斥(5):线程池的实现线程安全
  • Kronos金融时序预测模型终极指南:从入门到实战的完整教程
  • 从0到1掌握RAG技术:基于Dmeta-embedding-zh构建企业级知识库
  • 《超简单:用 Python 让 Excel 飞起来》读书笔记:2.2.3 数据类型查询:type排错法
  • 别再纠结选哪个了!2024年Unity热更新方案横向对比:XLua、ILRuntime、HybridCLR、Puerts到底怎么选?
  • Sora 2配音模型微调实战:用仅200条高质量中文配音样本,在3小时内完成角色音色迁移(含LoRA权重热加载代码)
  • 2026 年 6 月搭企业考试系统,选这款就够了 - 速递信息
  • 米哈游抽卡记录管理终极指南:如何永久保存和分析你的抽卡数据
  • 植物健康系统|基于SprinBoot+vue的植物健康系统平台系统(源码+数据库+文档)
  • 微信聊天记录永久保存终极指南:WeChatMsg完全免费的数据自主管理方案
  • 第二阶段Day05网络编程和多线程
  • 蓝牙串口模块AT指令配置实战:从HC-05原理到SH-B30应用
  • 三步搞定GitHub精准下载:DownGit终极解决方案
  • Linux RT 组调度:RT_GROUP_SCHED 的实时任务资源隔离
  • 别再盲目做增量预训练了!基于MedicalGPT项目,聊聊医疗大模型定制化的务实路线
  • 基于深度学习的路面裂缝检测系统(YOLOv12完整代码+论文示例+多算法对比)
  • 免费投票链接制作平台,投票小程序推荐 - 投票小程序
  • 论文答辩 PPT 别瞎熬了!okbiye AI PPT 按步骤来,半小时搞定
  • CANN/ops-blas stpttr算子实现
  • 如何3分钟快速掌握ChanlunX:通达信缠论自动化分析终极指南
  • 原神帧率解锁实战指南:从60帧到144帧的游戏体验革命
  • Sora 2编码参数“黑箱”破译(含H.266/VVC兼容性约束、LLM-guided rate分配协议、GPU显存感知型码控算法),全网首发技术白皮书节选
  • 电磁冷热量计源头厂家推荐榜:2026年国产十大品牌综合实力深度解析 - 液体流量液位品牌推荐
  • 服务器 数据恢复
  • 答辩 PPT 别再熬大夜了!用 okbiye AI PPT,论文党也能一键通关毕业季
  • 别再硬啃官方文档了!用Scanpy搞定单细胞分析,这份避坑指南帮你省下80%时间
  • TigerVNC终极实战指南:打造跨平台高效远程桌面环境
  • 如何永久保存微信聊天记录:三步搞定数据备份的完整指南