Linux CFS 的 throttled_cfs_rq:被限流任务组的管理与恢复

张开发
2026/4/13 20:25:41 15 分钟阅读

分享文章

Linux CFS 的 throttled_cfs_rq:被限流任务组的管理与恢复
一、简介在现代云计算和容器化环境中CPU资源的公平分配与限制是系统稳定性的关键保障。Linux内核的CFSCompletely Fair Scheduler带宽控制机制通过cpu.cfs_quota_us和cpu.cfs_period_uscgroup v2中统一为cpu.max为进程组提供硬性的CPU使用上限。当一个cgroup内的进程在指定周期内消耗完所有配额时内核会触发限流throttle机制将该cgroup关联的cfs_rqCFS运行队列标记为受限状态并将其加入全局的throttled_cfs_rq链表进行管理。掌握throttled_cfs_rq的管理与恢复机制对于以下场景至关重要云原生调度理解Kubernetes中CPU limit的底层实现原理性能调优诊断容器CPU throttling导致的延迟问题内核开发参与调度子系统的优化与bug修复学术研究分析实时系统的资源隔离与调度算法本文将深入解析内核源码通过实际案例演示限流触发、队列管理、配额恢复的全过程帮助读者建立对CFS带宽控制机制的系统性认知。二、核心概念2.1 带宽控制基础术语术语说明对应cgroup接口Period资源核算的时间窗口默认100mscpu.cfs_period_us/cpu.max第二个值Quota周期内允许使用的CPU时间-1表示无限制cpu.cfs_quota_us/cpu.max第一个值Runtime全局时间池的剩余配额内核内部维护Throttle配额耗尽后强制暂停调度cpu.stat中的throttled统计Unthrottle新周期开始后恢复调度自动或手动触发2.2 关键数据结构CFS带宽控制涉及两个核心结构体分别代表中心端任务组级和节点端CPU级// 中心端task_group级别的带宽管理 struct cfs_bandwidth { raw_spinlock_t lock; // 保护全局时间池的锁 ktime_t period; // 周期时长 u64 quota; // 周期配额 u64 runtime; // 全局时间池剩余时间 u64 runtime_expires;// 当前周期到期时间 struct hrtimer period_timer; // 周期定时器 struct hrtimer slack_timer; // 剩余时间回收定时器 struct list_head throttled_cfs_rq; // 被限流的cfs_rq链表核心 int nr_periods; // 周期计数 int nr_throttled; // 限流次数统计 u64 throttled_time; // 累计限流时间 bool distribute_running; // 是否正在分发时间 }; // 节点端per-CPU的CFS运行队列 struct cfs_rq { // ... 其他CFS字段 ... int runtime_enabled; // 是否启用带宽控制 u64 runtime_expires; // 本地到期时间 s64 runtime_remaining; // 本地剩余时间片 u64 throttled_clock; // 开始限流的时间戳 int throttled; // 限流状态标志 int throttle_count; // 层级限流计数 struct list_head throttled_list; // 用于挂入throttled_cfs_rq链表 };关键理解throttled_cfs_rq链表是连接中心端与节点端的桥梁——当某个CPU上的cfs_rq耗尽本地时间且无法从全局池获取配额时就会被挂入该链表等待周期重置后的恢复。2.3 限流与恢复的触发条件触发限流的条件需同时满足cfs_rq-runtime_remaining 0本地时间耗尽__assign_cfs_rq_runtime()返回false无法从全局池申请到配额当前不在限流状态!cfs_rq_throttled(cfs_rq)触发恢复的条件period_timer到期全局时间池runtime重置为quotadistribute_cfs_runtime()为cfs_rq分配了正数时间片或slack_timer回收了足够时间并触发异步恢复三、环境准备3.1 软硬件环境要求项目要求操作系统Linux 5.4推荐6.6 LTS或更新版本内核配置CONFIG_CFS_BANDWIDTHy、CONFIG_CGROUPSy文件系统cgroup v2挂载或v1的cpu控制器开发工具gcc、bpftrace、perf、ftrace权限root或具有CAP_SYS_ADMIN能力3.2 验证环境支持#!/bin/bash # 文件名check_env.sh # 功能验证系统是否支持CFS带宽控制 echo CFS Bandwidth Control 环境检查 # 1. 检查内核配置 echo [1/5] 检查内核配置... if zgrep -q CONFIG_CFS_BANDWIDTHy /proc/config.gz 2/dev/null || \ grep -q CONFIG_CFS_BANDWIDTHy /boot/config-$(uname -r) 2/dev/null; then echo ✅ 内核已启用CFS_BANDWIDTH else echo ❌ 内核未启用CFS_BANDWIDTH需要重新编译内核 exit 1 fi # 2. 检查cgroup挂载 echo [2/5] 检查cgroup挂载... if mount | grep -q cgroup2; then echo ✅ cgroup v2已挂载 CGROUP_Vv2 elif mount | grep -q cgroup.*cpu; then echo ✅ cgroup v1 (cpu控制器)已挂载 CGROUP_Vv1 else echo ❌ cgroup未正确挂载 exit 1 fi # 3. 检查带宽控制接口 echo [3/5] 检查带宽控制接口... if [ $CGROUP_V v2 ]; then if [ -f /sys/fs/cgroup/cpu.max ]; then echo ✅ v2 cpu.max接口存在 fi else if [ -d /sys/fs/cgroup/cpu ]; then echo ✅ v1 cpu控制器存在 fi fi # 4. 检查内核参数 echo [4/5] 检查可调参数... if [ -f /proc/sys/kernel/sched_cfs_bandwidth_slice_us ]; then echo ✅ sched_cfs_bandwidth_slice_us: $(cat /proc/sys/kernel/sched_cfs_bandwidth_slice_us) us else echo ⚠️ 内核参数接口不存在可能为旧版本 fi # 5. 检查调试工具 echo [5/5] 检查调试工具... command -v bpftrace /dev/null 21 echo ✅ bpftrace已安装 || echo ⚠️ bpftrace未安装 command -v perf /dev/null 21 echo ✅ perf已安装 || echo ⚠️ perf未安装 echo echo 环境检查完成 3.3 测试环境搭建# 创建测试cgroupv2示例 sudo mkdir -p /sys/fs/cgroup/test_throttle echo cpu | sudo tee /sys/fs/cgroup/cgroup.subtree_control # 设置严格限制每100ms只能使用50ms CPU0.5核 echo 50000 100000 | sudo tee /sys/fs/cgroup/test_throttle/cpu.max # 创建CPU密集型测试程序 cat /tmp/cpu_burn.c EOF #include stdio.h #include unistd.h #include sys/types.h int main() { printf(PID: %d\n, getpid()); volatile long counter 0; while (1) { counter; // 每1亿次循环打印一次避免输出过多 if (counter % 100000000 0) { printf(Counter: %ld\n, counter); } } return 0; } EOF gcc -o /tmp/cpu_burn /tmp/cpu_burn.c -O2四、应用场景在多租户容器平台中CFS带宽控制与throttled_cfs_rq机制发挥着关键的资源隔离作用。假设一个场景某云服务商的Kubernetes集群运行着电商应用延迟敏感和日志处理批处理两类工作负载。平台管理员为每个Namespace设置CPU limit如电商Pod限制为1核cpu.max 100000 100000日志处理限制为0.5核。当电商应用遭遇流量洪峰时其CPU使用迅速达到limit阈值此时内核触发throttle机制该Pod对应cgroup的cfs_rq被移出调度红黑树加入throttled_cfs_rq链表进程被强制等待。这虽然保护了节点其他租户但会导致请求延迟飙升。通过理解throttled_cfs_rq的恢复时机下一个period开始管理员可以调整period长度如从100ms改为10ms来缩短冷冻期或开启cpu.max.burst允许短期突发。同时监控cpu.stat中的throttled_usec指标可以量化throttle对业务的影响指导资源limit的合理设置。五、实际案例与步骤5.1 案例一观察throttle触发与队列管理目标观察cfs_rq被加入throttled_cfs_rq链表的全过程。#!/bin/bash # 文件名case1_throttle_trigger.sh # 功能演示throttle触发过程 set -e CGROUP_PATH/sys/fs/cgroup/test_throttle TEST_PROG/tmp/cpu_burn echo 案例1Throttle触发与队列管理观察 # 步骤1创建cgroup并设置严格限制0.1核 echo [步骤1] 创建cgroup设置quota10ms/period100ms... sudo mkdir -p $CGROUP_PATH 2/dev/null || true echo 10000 100000 | sudo tee $CGROUP_PATH/cpu.max echo 1 | sudo tee $CGROUP_PATH/cpu.weight # 步骤2启动CPU密集型进程并加入cgroup echo [步骤2] 启动测试进程... $TEST_PROG PID$! sleep 1 # 等待进程启动 echo $PID | sudo tee $CGROUP_PATH/cgroup.procs echo 测试进程PID: $PID已加入cgroup # 步骤3监控throttle统计 echo [步骤3] 监控throttle状态持续10秒... echo 时间戳 | nr_periods | nr_throttled | throttled_time for i in {1..10}; do STAT$(cat $CGROUP_PATH/cpu.stat 2/dev/null | grep -E nr_periods|throttled | tr \n ) echo $(date %H:%M:%S) | $STAT sleep 1 done # 步骤4使用perf观察调度事件如果perf可用 if command -v perf /dev/null 21; then echo echo [步骤4] 使用perf采样sched_stat_throttled事件5秒... sudo perf stat -e sched:sched_stat_throttled -p $PID -- sleep 5 21 || true fi # 清理 echo [清理] 终止测试进程... kill $PID 2/dev/null || true wait $PID 2/dev/null || true echo echo 观察要点 echo 1. nr_throttled应随时间增加表示发生多次限流 echo 2. throttled_time表示累计被限流的时间纳秒 echo 3. 当quota(10ms)耗尽后进程必须等待下一个period(100ms)开始代码说明cpu.max 10000 100000表示每100ms周期内只能使用10ms CPU10%限制cpu.stat文件提供nr_periods周期计数、nr_throttled限流次数、throttled_usec累计限流时间三个关键指标5.2 案例二分析内核throttle路径目标通过ftrace跟踪throttle_cfs_rq和unthrottle_cfs_rq的调用。#!/bin/bash # 文件名case2_ftrace_throttle.sh # 功能使用ftrace跟踪throttle/unthrottle路径 set -e echo 案例2内核throttle路径跟踪 # 步骤1挂载debugfs sudo mount -t debugfs none /sys/kernel/debug 2/dev/null || true # 步骤2启用sched相关tracepoint echo [步骤1] 启用sched:sched_stat_throttled tracepoint... echo 1 | sudo tee /sys/kernel/debug/tracing/events/sched/sched_stat_throttled/enable 2/dev/null || { echo ⚠️ tracepoint不可用尝试使用kprobe... # 备用方案使用kprobe跟踪throttle_cfs_rq sudo sh -c echo p:throttle_enter throttle_cfs_rq /sys/kernel/debug/tracing/kprobe_events sudo sh -c echo p:throttle_exit unthrottle_cfs_rq /sys/kernel/debug/tracing/kprobe_events echo 1 | sudo tee /sys/kernel/debug/tracing/events/kprobes/throttle_enter/enable echo 1 | sudo tee /sys/kernel/debug/tracing/events/kprobes/throttle_exit/enable } # 步骤3清空trace buffer echo | sudo tee /sys/kernel/debug/tracing/trace # 步骤4启动测试负载 echo [步骤2] 启动测试负载... CGROUP_PATH/sys/fs/cgroup/test_throttle2 sudo mkdir -p $CGROUP_PATH 2/dev/null || true echo cpu | sudo tee /sys/fs/cgroup/cgroup.subtree_control /dev/null 21 || true echo 20000 100000 | sudo tee $CGROUP_PATH/cpu.max # 20%限制 # 启动4个CPU密集型线程 for i in {1..4}; do taskset -c 0 /tmp/cpu_burn echo $! | sudo tee $CGROUP_PATH/cgroup.procs /dev/null done BG_PIDS$! # 步骤5采集trace echo [步骤3] 采集trace数据5秒... sleep 5 sudo cat /sys/kernel/debug/tracing/trace | head -100 /tmp/throttle_trace.txt echo Trace数据已保存到/tmp/throttle_trace.txt # 步骤6分析trace echo [步骤4] 分析trace数据... echo --- throttle_cfs_rq调用统计 --- grep -c throttle_cfs_rq /tmp/throttle_trace.txt 2/dev/null || echo 0 echo --- unthrottle_cfs_rq调用统计 --- grep -c unthrottle_cfs_rq /tmp/throttle_trace.txt 2/dev/null || echo 0 # 显示部分原始trace echo --- 原始trace样本前20行--- head -20 /tmp/throttle_trace.txt # 清理 echo [清理] 停止跟踪... kill $BG_PIDS 2/dev/null || true echo 0 | sudo tee /sys/kernel/debug/tracing/events/sched/sched_stat_throttled/enable 2/dev/null || true echo | sudo tee /sys/kernel/debug/tracing/trace sudo rmdir $CGROUP_PATH 2/dev/null || true5.3 案例三period_timer与配额恢复机制目标观察period_timer到期后如何从throttled_cfs_rq链表恢复cfs_rq。/* * 内核关键函数解析do_sched_cfs_period_timer * 位于 kernel/sched/fair.c * * 这是周期定时器到期时执行的函数负责 * 1. 重置全局时间池runtime * 2. 遍历throttled_cfs_rq链表分发时间 * 3. 调用unthrottle_cfs_rq解除限流 */ static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun) { int throttled; // 1. 检查是否配置了带宽限制 if (cfs_b-quota RUNTIME_INF) goto out_deactivate; // 无限制直接退出 // 2. 检查是否有被限流的cfs_rq throttled !list_empty(cfs_b-throttled_cfs_rq); cfs_b-nr_periods overrun; // 累计周期数 // 3. 重置全局时间池将runtime设为quota __refill_cfs_bandwidth_runtime(cfs_b); // 4. 如果没有被限流的队列且处于idle状态停掉定时器 if (cfs_b-idle !throttled) goto out_deactivate; if (!throttled) { cfs_b-idle 1; return 0; } // 5. 统计限流周期数 cfs_b-nr_throttled overrun; // 6. 关键循环分发时间并解除限流 while (throttled cfs_b-runtime 0) { raw_spin_unlock(cfs_b-lock); // distribute_cfs_runtime遍历throttled_cfs_rq链表 throttled distribute_cfs_runtime(cfs_b); raw_spin_lock(cfs_b-lock); } cfs_b-idle 0; return 0; } /* * distribute_cfs_runtime分发时间给被限流的cfs_rq * 遍历throttled_cfs_rq链表为每个cfs_rq分配runtime_remaining */ static u64 distribute_cfs_runtime(struct cfs_bandwidth *cfs_b) { struct cfs_rq *cfs_rq; u64 runtime, remaining 1; rcu_read_lock(); // 遍历throttled_cfs_rq链表RCU保护 list_for_each_entry_rcu(cfs_rq, cfs_b-throttled_cfs_rq, throttled_list) { struct rq *rq rq_of(cfs_rq); struct rq_flags rf; rq_lock(rq, rf); if (!cfs_rq_throttled(cfs_rq)) goto next; // 已经被解除限流跳过 // 计算需要分配的时间当前负值1ns缓冲 runtime -cfs_rq-runtime_remaining 1; if (runtime remaining) runtime remaining; remaining - runtime; // 分配时间片 cfs_rq-runtime_remaining runtime; cfs_rq-runtime_expires cfs_b-runtime_expires; // 如果分配后时间片为正解除限流 if (cfs_rq-runtime_remaining 0) unthrottle_cfs_rq(cfs_rq); next: rq_unlock(rq, rf); if (!remaining) break; // 全局时间池耗尽 } rcu_read_unlock(); return remaining; }测试脚本#!/bin/bash # 文件名case3_period_timer.sh # 功能观察period_timer与配额恢复 echo 案例3Period Timer与配额恢复机制 # 调整slice大小以观察效果可选 # echo 10000 | sudo tee /proc/sys/kernel/sched_cfs_bandwidth_slice_us CGROUP_PATH/sys/fs/cgroup/test_period sudo mkdir -p $CGROUP_PATH 2/dev/null || true echo cpu | sudo tee /sys/fs/cgroup/cgroup.subtree_control /dev/null 21 || true # 设置周期为500ms配额为100ms20%限制便于观察 echo 100000 500000 | sudo tee $CGROUP_PATH/cpu.max echo [步骤1] 配置period500ms, quota100ms (20% CPU) # 启动测试进程 taskset -c 0 /tmp/cpu_burn PID$! sleep 1 echo $PID | sudo tee $CGROUP_PATH/cgroup.procs /dev/null echo [步骤2] 监控配额恢复过程每100ms采样一次持续5秒... # 使用bpftrace监控throttle/unthrottle事件如果可用 if command -v bpftrace /dev/null 21; then sudo timeout 5 bpftrace -e tracepoint:sched:sched_stat_throttled { printf(THROTTLE: pid%d, delay%llu us\n, pid, args-delay / 1000); } 2/dev/null || true else # 备用通过cpu.stat观察 for i in {1..50}; do THROTTLED$(grep nr_throttled $CGROUP_PATH/cpu.stat | awk {print $2}) TIME$(grep throttled_usec $CGROUP_PATH/cpu.stat | awk {print $2}) echo $(date %s.%N) nr_throttled$THROTTLED throttled_usec$TIME sleep 0.1 done fi kill $PID 2/dev/null || true sudo rmdir $CGROUP_PATH 2/dev/null || true echo echo 分析要点 echo 1. 每500ms period到期nr_throttled会增加 echo 2. throttled_usec的增量反映实际被限流的时间 echo 3. 如果quota(100ms)在周期内耗尽剩余400ms处于throttle状态5.4 案例四slack_timer与剩余时间回收目标理解slack_timer如何回收未使用的runtime并触发异步恢复。/* * __return_cfs_rq_runtime将cfs_rq的剩余时间返还给全局池 * 当任务出队如睡眠、退出时调用 */ static void __return_cfs_rq_runtime(struct cfs_rq *cfs_rq) { struct cfs_bandwidth *cfs_b tg_cfs_bandwidth(cfs_rq-tg); // 保留最小时间片默认1ms避免频繁申请 s64 slack_runtime cfs_rq-runtime_remaining - min_cfs_rq_runtime; if (slack_runtime 0) return; // 剩余时间太少不回收 raw_spin_lock(cfs_b-lock); if (cfs_b-quota ! RUNTIME_INF) { cfs_b-runtime slack_runtime; // 返还全局池 // 如果全局池有足够时间且有被限流的cfs_rq启动slack_timer if (cfs_b-runtime sched_cfs_bandwidth_slice() !list_empty(cfs_b-throttled_cfs_rq)) start_cfs_slack_bandwidth(cfs_b); } raw_spin_unlock(cfs_b-lock); cfs_rq-runtime_remaining - slack_runtime; }测试脚本#!/bin/bash # 文件名case4_slack_timer.sh # 功能演示slack_timer的剩余时间回收 echo 案例4Slack Timer与剩余时间回收 CGROUP_PATH/sys/fs/cgroup/test_slack sudo mkdir -p $CGROUP_PATH 2/dev/null || true echo cpu | sudo tee /sys/fs/cgroup/cgroup.subtree_control /dev/null 21 || true # 设置限制200ms/500ms40% echo 200000 500000 | sudo tee $CGROUP_PATH/cpu.max echo [步骤1] 启动间歇性CPU负载工作2秒睡眠2秒... # 创建间歇性负载脚本 cat /tmp/intermittent_load.sh EOF #!/bin/bash while true; do # CPU密集型工作2秒 timeout 2 taskset -c 0 /tmp/cpu_burn /dev/null 21 PID$! wait $PID 2/dev/null # 睡眠2秒让出CPU sleep 2 done EOF chmod x /tmp/intermittent_load.sh # 启动测试 taskset -c 0 /tmp/intermittent_load.sh BG_PID$! sleep 1 echo $BG_PID | sudo tee $CGROUP_PATH/cgroup.procs /dev/null echo [步骤2] 监控回收效果持续20秒... echo 时间 | 全局runtime(估算) | throttled次数 | 限流时间(us) for i in {1..20}; do # 读取统计 STATS$(cat $CGROUP_PATH/cpu.stat 2/dev/null) THROTTLED$(echo $STATS | grep nr_throttled | awk {print $2}) TIME$(echo $STATS | grep throttled_usec | awk {print $2}) echo $(date %H:%M:%S) | N/A | $THROTTLED | $TIME sleep 1 done kill $BG_PID 2/dev/null || true pkill -f cpu_burn 2/dev/null || true sudo rmdir $CGROUP_PATH 2/dev/null || true echo echo 原理解析 echo 当测试进程sleep时其cfs_rq的runtime_remaining会被部分返还到全局池 echo 如果此时throttled_cfs_rq链表非空slack_timer会被启动 echo slack_timer到期后会调用distribute_cfs_runtime尝试提前解除限流六、常见问题与解答Q1为什么设置了quota但进程仍然使用了超过限制的CPU原因分析Burst机制cgroup v2的cpu.max.burst允许短期突破限制统计粒度cpu.stat的统计可能因内核版本而异多核聚合quota是跨CPU聚合的多核场景下单核可能短暂超限诊断方法# 检查是否启用了burstv2 cat /sys/fs/cgroup/$CGROUP/cpu.max.burst # 查看详细统计 watch -n 1 cat /sys/fs/cgroup/$CGROUP/cpu.statQ2throttled_cfs_rq链表中的cfs_rq是如何排序的解答根据内核源码插入位置取决于distribute_running标志如果正在分发时间distribute_running1插入链表头部——避免当前分发过程看到新加入的cfs_rq否则插入链表尾部——保证FIFO顺序防止饥饿// 来自throttle_cfs_rq函数 if (cfs_b-distribute_running) list_add_rcu(cfs_rq-throttled_list, cfs_b-throttled_cfs_rq); else list_add_tail_rcu(cfs_rq-throttled_list, cfs_b-throttled_cfs_rq);Q3如何降低throttle带来的延迟影响解决方案缩短period减小cpu.cfs_period_usv1或使用更短的period值v2但会增加调度开销增大quota确保quota足够覆盖突发流量启用burstv2echo 50000 cpu.max.burst允许50ms的突发调整slice增大sched_cfs_bandwidth_slice_us默认5ms减少申请频率Q4内核6.x版本对throttle机制有何改进改进点基于内核6.6源码异步unthrottle引入unthrottle_cfs_rq_async在非本地CPU上通过throttled_csd_list延迟处理减少锁竞争Task-based throttleRFC补丁提出将throttle延迟到用户态返回时执行避免内核态持有资源被限流PELT时钟冻结优化更精确地处理限流期间的负载计算Q5如何监控throttle事件对业务的影响监控脚本#!/bin/bash # 实时监控cgroup的throttle情况 CGROUP$1 INTERVAL${2:-1} if [ -z $CGROUP ]; then echo 用法: $0 cgroup路径 [采样间隔秒] exit 1 fi echo 时间,周期数,限流次数,限流时间(us),限流占比(%) PREV_THROTTLED0 PREV_TIME0 while true; do STATS$(cat $CGROUP/cpu.stat) PERIODS$(echo $STATS | grep nr_periods | awk {print $2}) THROTTLED$(echo $STATS | grep nr_throttled | awk {print $2}) TIME$(echo $STATS | grep throttled_usec | awk {print $2}) if [ -n $PREV_TIME ] [ $PREV_TIME -gt 0 ]; then DELTA_TIME$((TIME - PREV_TIME)) DELTA_PERIOD$((PERIODS - PREV_PERIODS)) if [ $DELTA_PERIOD -gt 0 ]; then # 计算限流占比假设period为100ms THROTTLE_PCT$(echo scale2; $DELTA_TIME / ($DELTA_PERIOD * 100000) * 100 | bc) echo $(date %H:%M:%S),$PERIODS,$THROTTLED,$TIME,$THROTTLE_PCT% fi fi PREV_PERIODS$PERIODS PREV_THROTTLED$THROTTLED PREV_TIME$TIME 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. 使用bpftrace获取细粒度统计sudo bpftrace -e tracepoint:sched:sched_stat_throttled { throttle_ns[comm] hist(args-delay); } 3. 内核日志分析# 开启调度调试需重新编译内核开启CONFIG_SCHED_DEBUG echo Y /sys/kernel/debug/sched/debug7.2 性能优化建议1. 合理选择period长度场景推荐period说明低延迟应用Web服务10-50ms快速恢复减少throttle延迟批处理任务100-1000ms减少上下文切换提高吞吐混合负载100ms默认平衡延迟与开销2. 避免quota碎片化# 不推荐quota 1ms调度器开销占比过高 echo 1000 cpu.cfs_quota_us # 推荐最小quota至少为1ms最好是slice的整数倍 echo 10000 cpu.cfs_quota_us # 至少10ms3. 结合cpuset优化# 将任务绑定到特定CPU减少缓存失效 sudo sh -c echo 0-3 /sys/fs/cgroup/cpuset/mygroup/cpuset.cpus7.3 生产环境配置模板Web服务容器延迟敏感# cgroup v2配置 echo 100000 100000 cpu.max # 基础限制1核 echo 100000 cpu.max.burst # 允许突发1核 echo 100 cpu.weight # 相对权重 echo 0-3 cpuset.cpus # 绑定到前4核批处理任务吞吐量优先# cgroup v1配置 echo 1000000 cpu.cfs_period_us # 1秒周期 echo 800000 cpu.cfs_quota_us # 80% CPU echo 0 cpu.cfs_burst_us # 禁用burst严格限制7.4 内核参数调优# 调整CFS带宽控制slice大小默认5000us # 增大可减少锁竞争但可能增加单核分配延迟 echo 10000 /proc/sys/kernel/sched_cfs_bandwidth_slice_us # 查看当前配置 sysctl kernel.sched_cfs_bandwidth_slice_us八、总结与应用场景本文深入剖析了Linux CFS调度器中throttled_cfs_rq链表的管理与恢复机制核心要点包括限流触发当cfs_rq的runtime_remaining耗尽且无法从全局池cfs_b-runtime申请配额时throttle_cfs_rq()将cfs_rq加入throttled_cfs_rq链表并设置throttled1队列管理throttled_cfs_rq链表采用RCU保护插入位置根据distribute_running标志决定头部或尾部保证分发过程的正确性与公平性配额恢复period_timer到期后do_sched_cfs_period_timer()重置全局时间池调用distribute_cfs_runtime()遍历链表分发时间片时间片为正的cfs_rq通过unthrottle_cfs_rq()恢复调度异步优化内核6.x引入unthrottle_cfs_rq_async和throttled_csd_list减少跨CPU解除限流时的锁竞争应用场景回顾云原生资源隔离Kubernetes CPU limit的底层实现依赖本文所述机制理解其原理有助于合理设置limit避免过度限流实时系统调优通过调整period/quota参数在延迟与吞吐量之间取得平衡性能问题诊断利用cpu.stat和ftrace定位throttle导致的延迟问题建议读者在实际项目中结合bpftrace和perf工具进行动态分析并关注内核邮件列表中关于CFS带宽控制的最新优化如task-based throttle RFC持续跟踪该领域的技术演进。

更多文章