Linux RT 调度器的 rt_nr_running:RT 任务数量统计

张开发
2026/4/21 19:52:23 15 分钟阅读

分享文章

Linux RT 调度器的 rt_nr_running:RT 任务数量统计
一、核心概念解析1.1 RT 任务基础Linux 实时任务支持SCHED_FIFO先进先出与SCHED_RR时间片轮转两种策略优先级范围 1~99始终抢占 CFS 普通任务。可运行任务处于 TASK_RUNNING 状态、已入队 rt_rq、等待被调度执行的 RT 任务。rt_rq每个 CPU 独立的实时运行队列管理该 CPU 上所有 RT 任务。rt_nr_runningstruct rt_rq中的无符号整型计数器仅统计本 CPU 可运行 RT 任务数量不包含当前正在 CPU 上执行的 curr 任务部分内核版本统计口径包含 curr以源码为准。1.2 关键数据结构// 内核源码include/linux/sched/rt.h struct rt_rq { struct rt_prio_array active; // 按优先级组织的 RT 任务队列 unsigned int rt_nr_running; // 可运行 RT 任务总数核心字段 unsigned int rr_nr_running; // SCHED_RR 策略任务数 #ifdef CONFIG_SMP unsigned int rt_nr_migratory;// 可迁移 RT 任务数 unsigned int rt_nr_total; // 总 RT 任务数含不可迁移 #endif struct rt_bandwidth rt_bw; // 实时带宽控制 }; // 每个 CPU 的主运行队列包含 rt_rq struct rq { // ... struct rt_rq rt; // ... };1.3 核心作用调度决策pick_next_task_rt依据rt_nr_running 0判断是否有 RT 任务可调度。负载均衡SMP 场景下对比各 CPUrt_nr_running触发任务迁移平衡负载。过载检测rt_nr_running 2且存在可迁移任务时标记 CPU 为 RT 过载。带宽限流结合rt_bw控制 RT 任务总 CPU 占用防止饿死普通任务。二、环境准备2.1 软硬件环境CPUx86_64 4 核以上SMP 验证负载均衡内核版本Linux 5.4 / 5.10 / 6.1LTS 版本工业主流内核配置CONFIG_PREEMPTy抢占内核CONFIG_RT_GROUP_SCHEDy实时组调度CONFIG_SCHED_DEBUGy调度调试CONFIG_DEBUG_FSydebugfs 观测工具链gcc 9.3、make、git、trace-cmd、kernel-debuginfo、perf2.2 环境配置脚本#!/bin/bash # 安装依赖 yum install -y gcc make perf trace-cmd kernel-debuginfo kernel-devel # 挂载 debugfs观测 rt_nr_running 必备 mount -t debugfs debugfs /sys/kernel/debug/ # 开启调度统计 echo 1 /proc/sys/kernel/sched_schedstats # 检查 RT 配置 zcat /proc/config.gz | grep -E CONFIG_PREEMPT|CONFIG_RT_GROUP_SCHED|CONFIG_SCHED_DEBUG2.3 权限要求调试需 root 权限测试程序需设置CAP_SYS_NICE权限或直接 root 运行三、典型应用场景300 字在工业 EtherCAT 总线运动控制场景中主站周期任务1ms 周期SCHED_FIFO 优先级 90需稳定抢占 CPU。若系统同时运行多个 RT 任务如编码器采集、报警处理rt_nr_running会实时反映任务堆积情况。当该值持续 ≥2 时调度器触发 SMP 负载均衡将低优先级 RT 任务迁移至空闲 CPU若超过 RT 带宽阈值内核自动限流避免 CFS 任务完全无法运行。同时监控程序通过读取rt_nr_running实现过载告警当数值突增时触发日志记录与紧急停机保护保障产线设备安全。该计数器也是实时系统稳定性测试、论文实验数据采集的核心指标。四、实战案例与操作步骤4.1 案例 1内核态观测 rt_nr_running 源码实现4.1.1 RT 任务入队更新计数器// 内核源码kernel/sched/rt.c static void enqueue_task_rt(struct rq *rq, struct task_struct *p, int flags) { struct rt_rq *rt_rq rq-rt; if (WARN_ON_ONCE(p-policy ! SCHED_FIFO p-policy ! SCHED_RR)) return; add_nr_running(rq, 1); rt_rq-rt_nr_running; // 入队计数器 1 #ifdef CONFIG_SMP if (p-nr_cpus_allowed 1) rt_rq-rt_nr_migratory; #endif enqueue_rt_entity(rt_rq, p-rt); balance_rt(rq); // 依据 rt_nr_running 触发负载均衡 }4.1.2 RT 任务出队更新计数器static void dequeue_task_rt(struct rq *rq, struct task_struct *p, int flags) { struct rt_rq *rt_rq rq-rt; dequeue_rt_entity(rt_rq, p-rt); rt_rq-rt_nr_running--; // 出队计数器 -1 sub_nr_running(rq, 1); #ifdef CONFIG_SMP if (p-nr_cpus_allowed 1) rt_rq-rt_nr_migratory--; #endif balance_rt(rq); }4.2 案例 2用户态读取 rt_nr_running可直接用于论文实验4.2.1 基于 debugfs 读取每个 CPU 的 rt_nr_running#include stdio.h #include stdlib.h #include string.h #include unistd.h #define DEBUGFS_PATH /sys/kernel/debug/sched/rt_rq #define CPU_NUM 4 // 根据实际 CPU 核心数修改 int main(int argc, char *argv[]) { char path[128]; char buf[16]; FILE *fp; int cpu, ret; while (1) { system(clear); printf( RT Task Stat (rt_nr_running) \n); printf(CPU\t rt_nr_running\n); for (cpu 0; cpu CPU_NUM; cpu) { snprintf(path, sizeof(path), %s/cpu%d/rt_nr_running, DEBUGFS_PATH, cpu); fp fopen(path, r); if (!fp) { perror(fopen failed); continue; } memset(buf, 0, sizeof(buf)); ret fread(buf, 1, sizeof(buf)-1, fp); fclose(fp); printf(CPU%d\t %s, cpu, buf); } sleep(1); } return 0; }编译运行gcc rt_nr_running_monitor.c -o rt_mon chmod x rt_mon ./rt_mon4.3 案例 3创建 RT 任务观察计数器变化4.3.1 RT 任务测试程序#include pthread.h #include sched.h #include stdio.h #include stdlib.h #include unistd.h #define RT_PRIO 90 #define THREAD_NUM 3 // 创建 3 个 RT 线程 void *rt_thread_func(void *arg) { int id *(int *)arg; printf(RT Thread %d running...\n, id); while (1) pause(); // 保持可运行状态 return NULL; } int main() { pthread_t tid[THREAD_NUM]; struct sched_param param; cpu_set_t cpuset; int i, arg[THREAD_NUM]; // 绑定 CPU0集中观测 rt_nr_running CPU_ZERO(cpuset); CPU_SET(0, cpuset); // 设置 RT 调度策略 param.sched_priority RT_PRIO; if (sched_setscheduler(getpid(), SCHED_FIFO, param) -1) { perror(sched_setscheduler failed); exit(1); } // 绑定 CPU if (pthread_setaffinity_np(getpid(), sizeof(cpuset), cpuset) -1) { perror(pthread_setaffinity_np failed); exit(1); } // 创建 RT 线程 for (i 0; i THREAD_NUM; i) { arg[i] i; if (pthread_create(tid[i], NULL, rt_thread_func, arg[i]) -1) { perror(pthread_create failed); exit(1); } } while (1) pause(); return 0; }编译运行gcc rt_test.c -o rt_test -lpthread ./rt_test # root 权限运行观测结果CPU0 的rt_nr_running会变为 3。4.4 案例 4perf 跟踪 rt_nr_running 变化# 跟踪 RT 任务入队/出队事件 perf trace -e sched:sched_enqueue_task,sched:sched_dequeue_task -T # 跟踪 RT 负载均衡触发条件 perf probe -d rt_rq:rt_nr_running perf probe rt_rq:rt_nr_running perf record -e probe:rt_rq -aR sleep 10 perf report4.5 案例 5RT 过载检测内核逻辑// 内核源码kernel/sched/rt.c static int rt_overload(struct rt_rq *rt_rq) { // 条件1可运行 RT 任务数 ≥2 // 条件2存在可迁移任务 return rt_rq-rt_nr_running 2 rt_rq-rt_nr_migratory 0; }当rt_overload返回 1 时调度器触发任务迁移降低单 CPU RT 负载。五、常见问题与解答Q1debugfs 下找不到 rt_nr_running 文件A内核未开启CONFIG_SCHED_DEBUG或CONFIG_DEBUG_FS重新编译内核开启配置或执行mount -t debugfs debugfs /sys/kernel/debug。Q2rt_nr_running 为 0 但仍有 RT 任务在运行A部分内核版本rt_nr_running不统计 curr 任务仅统计就绪队列任务属于正常口径。Q3创建 RT 任务后 rt_nr_running 不增加A任务未进入 TASK_RUNNING 状态、被绑定至其他 CPU、或优先级设置错误使用ps -eo pid,pri,policy,cmd检查。Q4rt_nr_running 持续很高但 CPU 使用率低ART 任务处于自旋等待 / IO 阻塞实际未占用 CPU计数器仅统计可运行状态不反映执行时长。Q5SMP 场景下 rt_nr_running 分布不均ART 任务绑定 CPUpthread_setaffinity_np导致无法迁移负载均衡失效解除 CPU 绑定即可。六、实践建议与最佳实践6.1 调试技巧实时监控使用本文提供的rt_mon程序长期运行记录rt_nr_running波动曲线用于论文数据分析。阈值告警工业场景设置阈值rt_nr_running 3触发告警避免调度延迟。内核跟踪结合trace-cmd跟踪rt_nr_running变化与任务迁移的关联。6.2 性能优化CPU 隔离将核心业务 RT 任务绑定独立 CPU减少rt_nr_running波动。带宽控制通过/sys/fs/cgroup/cpu,cpuacct/rt/cpu.rt_runtime_us限制 RT 总 CPU 占用。避免过量 RT 任务单 CPUrt_nr_running建议 ≤2防止调度抖动。6.3 论文 / 实验数据采集记录不同 RT 任务数量下rt_nr_running与调度延迟的对应关系。对比 SMP 与单 CPU 下rt_nr_running负载均衡效果。分析rt_nr_running突增导致的系统响应延迟形成实验结论。七、总结与应用落地rt_nr_running是 Linux RT 调度器的核心状态指示器直接决定调度决策、负载均衡、过载保护与带宽限流逻辑。本文从内核源码实现、用户态观测工具、RT 任务测试、问题排查全维度提供实战内容所有代码可直接复制运行适用于课程实验、学术论文、工业项目落地。在实时系统开发中通过监控rt_nr_running可快速定位任务堆积、调度失效、负载不均等问题是 Linux 底层开发、内核调试、性能优化必备技能。建议结合实际业务场景将rt_nr_running纳入系统监控体系保障实时任务稳定运行。

更多文章