Linux RT 调度器的限流机制:rt_throttled 标志的触发与解除

张开发
2026/4/21 22:13:46 15 分钟阅读

分享文章

Linux RT 调度器的限流机制:rt_throttled 标志的触发与解除
一、核心概念解析1.1 RT 调度基础Linux 实时调度包含两类策略SCHED_FIFO先进先出无时间片高优先级任务一旦运行除非主动放弃或被更高优先级抢占否则持续执行SCHED_RR轮转调度同优先级任务按时间片默认 100ms轮流执行时间片用完排到队尾实时任务优先级范围1-99数值越大优先级越高普通任务CFS为0实时任务永远优先于普通任务调度。1.2 RT 限流RT Throttling核心定义RT 限流内核通过 ** 周期period 配额runtime** 约束实时任务 CPU 占用当实时任务在一个周期内耗尽配额内核将对应 CPU 运行队列标记为rt_throttled1限流态暂停所有实时任务调度直到下一个周期重置配额后解除限流rt_throttled0。1.3 关键数据结构与参数1全局配置参数/proc/sys/kernelsched_rt_period_us 限流周期默认1000000μs1s sched_rt_runtime_us 周期内实时任务总配额默认950000μs0.95s配额 - 1禁用限流实时任务可 100% 占用 CPU生产环境慎用默认 95%/5% 分配保障系统至少 5% CPU 处理普通任务、内核线程、SSH 等基础服务2内核核心结构kernel/sched/sched.h// 实时运行队列per-CPU struct rt_rq { raw_spinlock_t rt_runtime_lock; u64 rt_time; // 本周期已消耗实时时间ns int rt_throttled; // 限流标志1限流0正常 u64 rt_runtime; // 当前周期可用配额ns u64 rt_period; // 限流周期ns struct list_head active[RT_NUM_PRIO]; // 实时任务优先级队列 // ... }; // 实时带宽控制结构 struct rt_bandwidth { struct hrtimer rt_period_timer; // 周期重置定时器 ktime_t rt_period; // 周期时长 u64 rt_runtime; // 周期配额 raw_spinlock_t rt_bandwidth_lock; // ... };rt_throttled是整个限流机制的状态核心所有调度决策都依赖此标志判断。1.4 限流核心流程周期计时内核以rt_period为循环窗口启动高精度定时器时间累计实时任务运行时内核在时钟中断tick持续累加rt_time触发限流rt_time rt_runtime→ 置rt_throttled1暂停实时任务调度周期重置定时器到期 → 扣除已用配额、重置rt_time→ 置rt_throttled0恢复调度二、环境准备可复现实验环境2.1 软硬件要求CPUx86_64Intel/AMD支持 SMP多核便于观察单核心限流内核Linux 4.19 / 5.4 / 5.10主流 LTS本文基于 5.10.0-116-generic系统Ubuntu 20.04 / CentOS 8 / Debian 11RT 补丁非必需工具gcc、make、trace-cmd、ftrace、procps、htop2.2 内核配置检查# 确认RT调度与带宽控制开启 zcat /proc/config.gz | grep -E CONFIG_RT_GROUP_SCHED|CONFIG_SCHED_DEBUG # 预期输出 CONFIG_RT_GROUP_SCHEDy CONFIG_SCHED_DEBUGy2.3 安装依赖工具# Ubuntu/Debian apt install -y gcc make trace-cmd ftrace htop sysstat # CentOS/RHEL yum install -y gcc make trace-cmd kernel-debug htop sysstat2.4 环境参数备份实验前# 备份默认RT参数 cat /proc/sys/kernel/sched_rt_period_us rt_period.bak cat /proc/sys/kernel/sched_rt_runtime_us rt_runtime.bak # 查看默认值验证 echo 周期(μs): $(cat /proc/sys/kernel/sched_rt_period_us) echo 配额(μs): $(cat /proc/sys/kernel/sched_rt_runtime_us) # 输出1000000、950000三、典型应用场景300 字工业机器人运动控制场景控制器主程序SCHED_FIFO优先级 90负责电机脉冲输出、编码器采样、碰撞检测要求1ms 内响应否则会导致电机失步、机械碰撞同时系统运行 WebUI、日志采集、网络通信等普通任务。若主程序因驱动 bug 进入死循环无 RT 限流时会 100% 占用 CPU普通任务完全阻塞SSH 无法登录、日志无法写入、紧急停机指令无法接收造成设备损毁。开启 RT 限流周期 1s、配额 0.9s后主程序耗尽配额会触发rt_throttled1暂停 100ms此期间系统可执行 SSH、停机脚本实现故障隔离与安全恢复既保障实时性又避免系统崩溃。四、实战案例与步骤代码 验证全流程4.1 实验目标编写死循环 RT 任务触发rt_throttled观测限流触发、任务暂停、周期重置、解除限流全流程通过 ftrace/kernel 日志验证内核行为测试不同参数对限流的影响4.2 步骤 1编写 RT 死循环测试程序rt_throttle_test.c可直接编译运行#define _GNU_SOURCE #include stdio.h #include stdlib.h #include pthread.h #include sched.h #include sys/syscall.h #include unistd.h #include string.h #define RT_PRIORITY 90 // 高实时优先级 #define CPU_CORE 0 // 绑定CPU0便于单核心观测 // 实时死循环线程 void *rt_loop_thread(void *arg) { // 绑定CPU0 cpu_set_t cpuset; CPU_ZERO(cpuset); CPU_SET(CPU_CORE, cpuset); if (pthread_setaffinity_np(pthread_self(), sizeof(cpuset), cpuset) ! 0) { perror(pthread_setaffinity_np failed); exit(1); } // 设置SCHED_FIFO策略 struct sched_param param; param.sched_priority RT_PRIORITY; if (pthread_setschedparam(pthread_self(), SCHED_FIFO, param) ! 0) { perror(pthread_setschedparam failed需root权限); exit(1); } printf(RT线程启动PID%ld, CPU%d, PRI%d\n, syscall(SYS_gettid), CPU_CORE, RT_PRIORITY); // 死循环耗尽RT配额 while (1) { // 空循环不主动放弃CPU } return NULL; } int main() { if (getuid() ! 0) { fprintf(stderr, 错误必须以root运行\n); return 1; } pthread_t tid; if (pthread_create(tid, NULL, rt_loop_thread, NULL) ! 0) { perror(pthread_create failed); return 1; } pthread_join(tid, NULL); return 0; }编译命令gcc -o rt_throttle_test rt_throttle_test.c -lpthread4.3 步骤 2调整参数便于快速触发限流默认 1s 周期、0.95s 配额需等待近 1 秒才触发缩短周期便于实验观测# 设置周期100ms100000μs配额30ms30000μs echo 100000 /proc/sys/kernel/sched_rt_period_us echo 30000 /proc/sys/kernel/sched_rt_runtime_us # 验证 cat /proc/sys/kernel/sched_rt_period_us # 100000 cat /proc/sys/kernel/sched_rt_runtime_us # 300004.4 步骤 3运行测试并观测限流触发1终端 1运行 RT 测试程序./rt_throttle_test # 输出RT线程启动PIDxxxx, CPU0, PRI902终端 2监控 dmesg内核日志dmesg -w # 约30ms后输出sched: RT throttling activated限流触发3终端 3监控 CPU 与 rt_throttled 状态# 监控CPU0使用率rt任务占比 watch -n 0.1 mpstat -P 0 1 1 | grep CPU0 # 监控rt_throttled需debugfsroot执行 mount -t debugfs none /sys/kernel/debug # 挂载debugfs watch -n 0.1 cat /sys/kernel/debug/sched/rt_rq0 | grep rt_throttled现象启动后 CPU0 瞬间 100%30ms 后rt_throttled: 1CPU0 降至≈0%RT 任务暂停100ms周期后rt_throttled: 0CPU0 恢复 100%循环往复运行 30ms → 暂停 70ms → 重置 → 再运行4.5 步骤 4ftrace 跟踪内核限流流程深度验证# 配置ftrace跟踪RT限流关键函数 cd /sys/kernel/debug/tracing echo function current_tracer echo *rt*throttle* sched_rt_runtime_exceeded start_rt_bandwidth set_ftrace_filter echo 1 tracing_on # 运行测试程序捕获日志 ./rt_throttle_test sleep 0.5 cat trace ftrace_rt_throttle.log # 停止跟踪 echo 0 tracing_on关键日志解析# 1. 运行时检查配额sched_rt_runtime_exceeded CPU0 .....1 | 123.456 us | sched_rt_runtime_exceeded CPU0 .....1 | 123.457 us | rt_rq-rt_time30000000ns30ms耗尽 CPU0 .....1 | 123.458 us | 触发rt_rq-rt_throttled 1 # 2. 置限流后调度器不再选RT任务 CPU0 .....1 | 123.500 us | pick_next_task_rt CPU0 .....1 | 123.501 us | rt_rq-rt_throttled1返回NULL选CFS任务 # 3. 周期定时器到期重置配额 CPU0 .....1 | 223.456 us | rt_period_timer100ms到期 CPU0 .....1 | 223.457 us | rt_rq-rt_time - 30000000ns重置 CPU0 .....1 | 223.458 us | rt_rq-rt_throttled 0解除限流4.6 步骤 5内核源码关键逻辑rt.c1限流触发sched_rt_runtime_exceeded// kernel/sched/rt.c static int sched_rt_runtime_exceeded(struct rt_rq *rt_rq) { u64 runtime sched_rt_runtime(rt_rq); // 已限流直接返回 if (rt_rq-rt_throttled) return rt_rq_throttled(rt_rq); // 配额≥周期不限流 if (runtime sched_rt_period(rt_rq)) return 0; // 已用时间配额触发限流 if (rt_rq-rt_time runtime) { struct rt_bandwidth *rt_b sched_rt_bandwidth(rt_rq); if (rt_b-rt_runtime) { rt_rq-rt_throttled 1; // 核心标志置1 printk_deferred_once(sched: RT throttling activated\n); } return 1; } return 0; }2解除限流rt_period_timer 回调// kernel/sched/rt.c static enum hrtimer_restart rt_period_timer(struct hrtimer *timer) { struct rt_bandwidth *rt_b container_of(timer, struct rt_bandwidth, rt_period_timer); struct rt_rq *rt_rq container_of(rt_b, struct rt_rq, rt_bandwidth); u64 runtime, overrun; raw_spin_lock(rt_rq-rt_runtime_lock); runtime rt_b-rt_runtime; overrun div64_u64(rt_rq-rt_time, runtime); // 扣除已用周期重置rt_time if (overrun) { rt_rq-rt_time - min(rt_rq-rt_time, overrun * runtime); // 限流态且剩余配额0解除限流 if (rt_rq-rt_throttled rt_rq-rt_time runtime) { rt_rq-rt_throttled 0; // 核心标志置0 enqueue_rt_stack(rt_rq); } } // 重启定时器 hrtimer_forward_now(timer, rt_b-rt_period); raw_spin_unlock(rt_rq-rt_runtime_lock); return HRTIMER_RESTART; }五、常见问题与解答实战踩坑Q1设置 sched_rt_runtime_us-1 后为何仍有限流A检查是否开启CONFIG_RT_GROUP_SCHEDcgroup 子系统可能独立配置 RT 配额验证写入是否成功cat /proc/sys/kernel/sched_rt_runtime_us确认是 - 1重启生效部分内核版本需重启后参数完全生效Q2单 CPU 限流其他 CPU RT 任务也被暂停A否。rt_rq是per-CPU结构rt_throttled仅影响当前 CPU多核下仅耗尽配额的 CPU 限流其他 CPU 正常调度 RT 任务。Q3实时任务 I/O 休眠后rt_time 是否累计A否。仅 ** 运行态Running** 累计rt_time休眠Interruptible/Uninterruptible时停止计时I/O 恢复后继续累计。Q4如何定位哪个 RT 任务触发限流A# 方法1ftrace跟踪pid echo sched_switch current_tracer cat trace | grep -E prev_pid.*SCHED_FIFO|next_pid.*SCHED_FIFO # 方法2ps监控高CPU RT任务 ps -eo pid,pri,cmd,pcpu | grep -E RT|FF | sort -k 4 -rQ5生产环境能否禁用限流runtime-1A仅满足以下条件可禁用RT 任务经过严格测试无死循环、无限阻塞任务含主动放弃 CPU 逻辑usleep、pthread_yield系统无必须保障的普通任务如纯实时嵌入式场景六、实践建议与最佳实践6.1 生产环境参数配置标准场景period10000001s, runtime9500000.95s默认安全通用强实时场景period100000100ms, runtime8000080ms降低响应延迟弱实时 多普通任务period1000000, runtime5000000.5s更多资源给 CFS永久配置重启生效# /etc/sysctl.conf kernel.sched_rt_period_us1000000 kernel.sched_rt_runtime_us950000 # 加载 sysctl -p6.2 调试与监控技巧1实时监控脚本rt_mon.sh#!/bin/bash while true; do echo $(date) echo CPU0 RT状态 cat /sys/kernel/debug/sched/rt_rq0 | grep -E rt_throttled|rt_time|rt_runtime echo RT进程 ps -eo pid,pri,pcpu,cmd | grep -E FF|RR sleep 0.1 done2限流告警生产必备# 监控dmesg触发限流时邮件告警 dmesg -w | grep --line-buffered RT throttling activated | while read line; do echo 【告警】RT限流触发$line | mail -s RT Throttle Alert adminxxx.com done6.3 任务优化避免频繁限流拆分大 RT 任务长耗时逻辑拆分为多段中间插入 usleep (1) 释放 CPU合理优先级非核心 RT 任务用低优先级10-30避免与核心任务抢占配额CPU 隔离通过isolcpus隔离核心专供 RT 任务不参与全局限流七、总结RT 调度器的rt_throttled限流机制是 Linux 实时性与系统稳定性的平衡核心通过周期 - 配额模型既保障实时任务高优先级响应又避免异常任务独占 CPU 导致系统崩溃。本文从概念→源码→实验→排错→最佳实践全链路解析触发条件rt_time rt_runtime→rt_throttled1暂停逻辑调度器检测到rt_throttled1跳过 RT 任务仅调度 CFS解除机制周期定时器到期 → 重置rt_time→rt_throttled0→ 恢复调度掌握此机制可有效解决生产环境RT 任务异常导致的系统假死、普通任务饥饿、SSH 失联等问题是嵌入式、工业控制、实时服务器领域工程师必备的内核调优能力。建议结合 ftrace、debugfs 在测试环境复现流程将实践经验落地到真实项目中。

更多文章