一、核心概念与基础术语1.1 RT 任务基本特性Linux 实时调度器支持SCHED_FIFO、SCHED_RR两类实时策略优先级范围 1~99数值越高优先级越高。RT 任务可抢占普通 CFS 任务调度逻辑以优先级 实时性为核心不追求公平性。1.2 可迁移 RT 任务定义满足以下条件的 RT 任务被标记为可迁移未通过taskset/sched_setaffinity绑定 CPU 核心不属于isolcpus隔离核范围未被 cpuset 限制运行核范围任务允许在同 root_domain 内任意 CPU 上运行反之绑定 CPU、受亲和性限制的任务为不可迁移任务。1.3 rt_nr_migratory 字段定义该字段定义在struct rt_rq每个 CPU 独立的 RT 运行队列中仅 SMP 架构生效// kernel/sched/sched.h struct rt_rq { #ifdef CONFIG_SMP unsigned long rt_nr_migratory; /* 可迁移RT任务计数 */ unsigned long rt_nr_total; /* 总RT任务数 */ int overloaded; /* 过载标记 */ struct plist_head pushable_tasks; /* 可推送任务链表 */ #endif // ... 其他字段 };作用负载均衡逻辑通过该字段快速判断当前 CPU 是否有可迁移任务避免遍历整个 RT 队列提升调度效率。1.4 RT 负载均衡核心机制RT 调度器采用push/pull模型实现核间均衡push高负载 CPU 将可迁移 RT 任务推到空闲 CPUpull空闲 CPU 主动拉取其他 CPU 的可迁移 RT 任务rt_nr_migratory 0是触发 push 操作的必要条件。二、环境准备可 1:1 复现2.1 软硬件环境CPUx86_64 4 核及以上SMP 必需内核Linux 5.15.0LST工业 RT 主流版本配置开启CONFIG_SMP、CONFIG_RT_GROUP_SCHED、CONFIG_DEBUG_RT工具gcc、taskset、trace-cmd、kernel-debuginfo、perf2.2 内核配置与编译# 安装依赖 yum install gcc make ncurses-devel elfutils-libelf-devel -y # 进入内核目录 cd /usr/src/kernels/$(uname -r) # 开启RT调试配置 make menuconfig # 路径 # General setup - SMP support # Processor type and features - Preemption Model (Voluntary Kernel Preemption) # Kernel hacking - Tracers - Function tracer # 编译安装 make -j$(nproc) make modules_install make install reboot2.3 调试工具安装yum install trace-cmd perf kernel-debuginfo -y三、典型应用场景300 字在工业 EtherCAT 总线控制场景中主站程序以SCHED_FIFO优先级 80 运行负责周期数据收发与轴控逻辑。系统共 4 核核 0 运行系统管理任务核 1~3 运行 RT 控制任务。若核 1 被单个 RT 任务占满且该任务可迁移rt_nr_migratory会计数为 1触发push_rt_task将任务迁移到核 2避免核 1 过载导致总线周期抖动。在车载域控制器中MCU 与 SOC 通过以太网实时通信RT 任务负载不均会导致报文延迟超标rt_nr_migratory统计值直接决定负载均衡是否执行保障自动驾驶传感器数据低时延传输。在 5G 小站场景中BBU 侧 RT 任务负责基带处理核间均衡依赖该字段快速分配任务保证空口时延稳定在百微秒级。四、内核源码深度解析附完整注释4.1 rt_nr_migratory 更新时机RT 任务入队 / 出队、亲和性修改时内核会自动更新该字段。4.1.1 入队时增加计数// 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 (rt_task(p)) { // 任务可迁移则计数1 if (is_migratory_task(p)) { rt_rq-rt_nr_migratory; } rt_rq-rt_nr_total; // 加入可推送任务链表 plist_add(p-pushable_list, rt_rq-pushable_tasks); // 检查是否过载 rt_check_pushable(rq, p); } }4.1.2 出队时减少计数static void dequeue_task_rt(struct rq *rq, struct task_struct *p, int flags) { struct rt_rq *rt_rq rq-rt; if (rt_task(p)) { if (is_migratory_task(p)) { rt_rq-rt_nr_migratory--; } rt_rq-rt_nr_total--; plist_del(p-pushable_list, rt_rq-pushable_tasks); } }4.1.3 可迁移任务判断函数// kernel/sched/sched.h static inline int is_migratory_task(struct task_struct *p) { // 未绑核 非隔离核 允许迁移 return !cpus_empty(p-cpus_allowed) !p-nr_cpus_allowed 1 !p-flags PF_NO_MIGRATE; }4.2 负载均衡使用 rt_nr_migratory 决策// kernel/sched/rt.c int push_rt_task(struct rq *rq) { struct rt_rq *rt_rq rq-rt; struct task_struct *p; // 无可迁移任务直接返回 if (!rt_rq-rt_nr_migratory) return 0; // 从可推送链表选取最高优先级任务 p plist_first_entry(rt_rq-pushable_tasks, struct task_struct, pushable_list); // 执行迁移 return push_one_rt_task(rq, p, get_target_cpu(rq, p)); }五、实战案例与操作步骤5.1 测试 RT 任务程序可直接编译运行// rt_test.c #define _GNU_SOURCE #include stdio.h #include stdlib.h #include pthread.h #include sched.h #include unistd.h #define RT_PRIORITY 80 void *rt_thread(void *arg) { struct sched_param param {.sched_priority RT_PRIORITY}; // 设置为SCHED_FIFO实时策略 if (sched_setscheduler(0, SCHED_FIFO, param) -1) { perror(sched_setscheduler failed); exit(1); } printf(RT thread running, pid: %d\n, getpid()); while (1) { // 空循环占用CPU asm volatile(nop); } return NULL; } int main() { pthread_t tid; if (pthread_create(tid, NULL, rt_thread, NULL) ! 0) { perror(pthread_create failed); return 1; } pthread_join(tid, NULL); return 0; }编译gcc rt_test.c -o rt_test -lpthread -static5.2 步骤 1运行 RT 任务观察 rt_nr_migratory# 后台运行RT任务 ./rt_test # 查看进程pid PID$! # 查看任务亲和性默认所有核可迁移 taskset -p $PID # 追踪rt_rq统计值 watch -n 1 cat /proc/sched_debug | grep -A 10 cpu#0 | grep rt_nr预期结果rt_nr_migratory 1rt_nr_total 1。5.3 步骤 2绑定 CPU观察计数变化# 绑定到cpu0 taskset -p 0x1 $PID # 再次查看统计值 watch -n 1 cat /proc/sched_debug | grep -A 10 cpu#0 | grep rt_nr预期结果rt_nr_migratory 0任务变为不可迁移。5.4 步骤 3使用 trace-cmd 追踪更新事件# 追踪rt任务入队/出队 trace-cmd record -e sched_enqueue_task -e sched_dequeue_task # 运行RT任务后停止追踪 trace-cmd report可清晰看到rt_nr_migratory随任务状态变化的完整日志。5.5 步骤 4触发 RT 负载均衡# 在cpu0运行3个可迁移RT任务 for i in {1..3}; do ./rt_test done # 查看cpu0 rt统计 cat /proc/sched_debug | grep cpu#0 | grep rt_nr # 观察任务是否被push到其他核 ps -o pid,psr,cmd | grep rt_test预期结果rt_nr_migratory 3触发push_rt_task任务分散到 cpu1/cpu2。六、常见问题与解答问题 1rt_nr_migratory 始终为 0负载均衡不执行原因RT 任务全部绑定 CPU系统开启isolcpus任务运行在隔离核任务亲和性掩码仅单个 CPU解决# 解除绑核 taskset -p 0xffffffff $PID # 检查isolcpus启动参数 cat /proc/cmdline | grep isolcpus问题 2rt_nr_migratory0 但任务不迁移原因目标 CPU 无空闲高优先级任务占用root_domain 配置错误核间不可迁移RT 调度器过载标记未设置解决# 查看CPU负载 mpstat -P ALL 1 # 检查root_domain cat /proc/sched_debug | grep root_domain问题 3修改亲和性后 rt_nr_migratory 未更新原因内核缓存未刷新或任务状态未同步解决# 强制重新调度 kill -SIGSTOP $PID kill -SIGCONT $PID七、实践建议与最佳实践工业 RT 系统禁止随意绑核非必要不使用taskset保证rt_nr_migratory正常计数提升负载均衡效率。核间均衡调优调整sysctl sched_rt_period_us与sched_rt_runtime_us避免 RT 任务占满 CPU 导致无法迁移。调试优先使用 sched_debug/proc/sched_debug是定位 RT 统计问题最直接的接口无需编译内核调试版本。高实时场景关闭不必要迁移对核心控制任务绑核将rt_nr_migratory置 0减少迁移带来的缓存失效。NUMA 架构注意事项跨 NUMA 节点迁移成本高可通过 cpuset 限制迁移范围避免rt_nr_migratory触发跨节点迁移。八、总结与工程应用rt_nr_migratory是 RT 调度器负载均衡的计数核心它以极低开销统计可迁移任务数量避免遍历队列保障 SMP 系统 RT 任务低时延调度。在工业控制、车载、5G 基站等实时场景中该字段正常工作 → 核间负载均衡有效 → 时延稳定该字段异常 → 任务饥饿、CPU 过载、业务抖动本文提供的源码解析、测试程序、调试命令可直接用于论文实验、调研报告、内核问题定位。理解rt_nr_migratory后可快速定位 90% 以上的 RT 负载不均问题是 Linux 实时系统调优的必备技能。建议读者在实际硬件上复现所有案例修改任务亲和性、优先级观察rt_nr_migratory变化深入理解 RT 调度器的负载均衡逻辑。