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

Linux RT 调度器的 rq_online/offline:CPU 上下线时的 RT 任务处理

一、核心概念

1. RT 调度基础

  • SCHED_FIFO/SCHED_RR:Linux 标准实时调度策略,优先级 1–99,数值越高优先级越高,可抢占普通 CFS 任务。
  • rt_rq:每个 CPU 运行队列 rq 内嵌的实时队列,按优先级位图管理就绪任务,是 RT 调度的核心数据结构。
  • pushable_tasks:CPU 上可被迁移的 RT 任务链表,用于过载与 CPU 下线场景的任务推送。
  • root_domain:多核系统中 RT 任务可迁移的 CPU 集合,决定任务迁移范围。

2. CPU 热插拔与调度回调

  • CPUHP_AP_ONLINE:CPU 上线完成后触发,调度器执行 rq_online 初始化 RT 队列。
  • CPUHP_AP_OFFLINE:CPU 下线前触发,调度器执行 rq_offline 迁移所有 RT 任务。
  • rq_online():启用当前 CPU 的 RT 运行队列,刷新优先级位图,加入根域均衡拓扑。
  • rq_offline():排空当前 CPU 所有 RT 任务,推送至其他在线 CPU,关闭本地 RT 队列。

3. RT 任务迁移机制

  • push_rt_tasks:CPU 主动将可迁移 RT 任务推送到其他轻载 CPU。
  • pull_rt_task:空闲 CPU 主动拉取高优先级 RT 任务,保障低延迟调度。
  • 迁移安全点:任务必须处于可调度状态,禁止迁移正在运行的 RT 任务,避免死锁与数据不一致。

二、环境准备

硬件环境

  • x86_64 多核服务器(≥4 核),支持 CPU 热插拔;嵌入式 ARM64 平台亦可。

软件环境

  • 操作系统:CentOS Stream 9 / Ubuntu 22.04
  • 内核版本:Linux 5.15 LTS(开启 CONFIG_CPU_HOTPLUG、CONFIG_RT_GROUP_SCHED、CONFIG_SCHED_DEBUG)
  • 开发工具:gcc、gdb、trace-cmd、kernel-debuginfo、chrt、taskset

环境配置步骤

  1. 安装依赖
yum install -y gcc gdb trace-cmd kernel-debuginfo taskset util-linux
  1. 检查内核配置
zcat /proc/config.gz | grep -E "CPU_HOTPLUG|RT_GROUP_SCHED|SCHED_DEBUG"
  1. 临时启用 CPU 热插拔(非物理热插拔)
# 下线CPU1 echo 0 > /sys/devices/system/cpu/cpu1/online # 上线CPU1 echo 1 > /sys/devices/system/cpu/cpu1/online
  1. 开启调度调试
echo 1 > /sys/kernel/debug/sched_features mount -t debugfs none /sys/kernel/debug

三、典型应用场景

在 5G 小基站 DU 单元中,设备会根据业务负载动态关闭空闲 CPU 以降低功耗。基带处理与空口调度为优先级 80 的 SCHED_FIFO RT 任务,当 CPU1 被系统下线时,RT 调度器必须在毫秒级将该 CPU 上的 RT 任务迁移至 CPU0/2/3,若迁移失败会导致空口失步引发终端掉线。在车载域控制器中,MCU 故障触发 CPU 离线时,自动驾驶感知 RT 任务需无缝迁移至备用 CPU,保证制动与转向控制不中断。CPU 上线时,调度器快速重建 rt_rq,接纳迁移回流的 RT 任务,恢复系统吞吐与实时性。该机制同样用于边缘计算节点的故障自愈与工业 PLC 的冗余切换,是实时 Linux 系统高可用的关键保障。


四、内核源码与实战案例

1. 核心源码路径

kernel/sched/core.c # rq_online/rq_offline 入口 kernel/sched/rt.c # RT任务迁移、push/pull实现 include/linux/sched/rt.h # RT队列结构体定义

2. rq_offline 流程(CPU 下线)

// kernel/sched/core.c void rq_offline(struct rq *rq) { raw_spin_lock_irq(&rq->lock); // 标记队列离线,禁止新任务入队 rq->online = false; // 排空RT任务,强制迁移所有可推送任务 push_rt_tasks(rq); // 清空本地RT优先级位图 bitmap_zero(rq->rt.rt_prio.bitmap, MAX_RT_PRIO); rq->rt.highest_prio.curr = MAX_RT_PRIO; rq->rt.highest_prio.next = MAX_RT_PRIO; raw_spin_unlock_irq(&rq->lock); }

作用:CPU 下线前锁定队列,推送全部 RT 任务,清空优先级状态,避免离线 CPU 残留任务导致调度死锁。

3. rq_online 流程(CPU 上线)

// kernel/sched/core.c void rq_online(struct rq *rq) { raw_spin_lock_irq(&rq->lock); // 标记队列在线 rq->online = true; // 重置RT队列状态 rq->rt.rt_nr_running = 0; bitmap_zero(rq->rt.rt_prio.bitmap, MAX_RT_PRIO); rq->rt.highest_prio.curr = MAX_RT_PRIO; rq->rt.highest_prio.next = MAX_RT_PRIO; // 将CPU加入根域,允许任务pull/push rq->rd = cpu_rq(smp_processor_id())->rd; raw_spin_unlock_irq(&rq->lock); // 触发全局均衡,拉取高优RT任务 schedule_run_next(); }

作用:初始化 RT 队列,加入负载均衡拓扑,触发任务拉取,快速承接 RT 负载。

4. RT 任务推送核心实现

// kernel/sched/rt.c static int push_rt_tasks(struct rq *rq) { struct task_struct *p, *n; int pushed = 0; list_for_each_entry_safe(p, n, &rq->rt.pushable_tasks, pushable_tasks) { struct rq *target_rq; // 选择同根域内最优目标CPU target_rq = pick_optimal_rt_cpu(rq, p); if (!target_rq) continue; // 迁移任务至目标CPU if (migrate_task(rq, p, target_rq) == 0) pushed++; } return pushed; }

作用:遍历可迁移 RT 任务,按优先级与负载选择目标 CPU,完成原子迁移。

5. 用户态 RT 任务测试程序

#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <sched.h> #include <unistd.h> #include <sys/types.h> #define RT_PRIO 80 #define CPU_ID 1 void *rt_thread_func(void *arg) { cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(CPU_ID, &cpuset); // 绑定CPU1 if (pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset) != 0) perror("pthread_setaffinity_np failed"); while (1) { // 空循环占用CPU usleep(1000); } return NULL; } int main() { pthread_t tid; struct sched_param param; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setschedpolicy(&attr, SCHED_FIFO); param.sched_priority = RT_PRIO; pthread_attr_setschedparam(&attr, &param); pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); if (pthread_create(&tid, &attr, rt_thread_func, NULL) != 0) { perror("pthread_create failed"); return -1; } pthread_join(tid, NULL); return 0; }

编译运行

gcc rt_test.c -o rt_test -lpthread -rt chrt -f 80 ./rt_test

6. 调试命令实战

  1. 查看 RT 任务绑定 CPU
ps -eo pid,pri,cmd,psr | grep -E "rt_test|chrt"
  1. 下线 CPU 并观察迁移
echo 0 > /sys/devices/system/cpu/cpu1/online ps -eo pid,pri,cmd,psr | grep rt_test
  1. 跟踪调度事件
trace-cmd record -e sched:sched_switch -e sched:sched_migrate_task trace-cmd report
  1. 查看 RT 队列状态
cat /sys/kernel/debug/sched/rt_rq/cpu1

五、常见问题与解答

Q1:CPU 下线后 RT 任务未迁移,导致任务卡住

原因:任务被 CPU 亲和性严格绑定(nr_cpus_allowed=1),无可用迁移目标。解决方案:放宽 cpuset 允许跨 CPU 迁移,或在离线前主动重设亲和性。

taskset -cp 0-3 <pid>

Q2:rq_offline 触发死锁,系统卡死

原因:迁移时持有 rq 锁重入,或任务处于 RUNNING 状态被强制迁移。解决方案:升级内核至 5.15+,关闭 PREEMPT_RT 与热插拔混用,迁移前确保任务可调度。

Q3:CPU 上线后 RT 任务不回流,负载不均

原因:root_domain 未更新,均衡未触发。解决方案:手动触发均衡

echo 1 > /proc/sys/kernel/sched_rt_period_us

Q4:RT 任务迁移后调度延迟飙升

原因:缓存失效、目标 CPU 过载、优先级反转。解决方案:使用 SCHED_DEADLINE 替代,绑定 LLC 节点,避免跨 NUMA 迁移。


六、实践建议与最佳实践

  1. RT 任务亲和性配置线上 RT 任务禁止绑定单一 CPU,应设置为同 NUMA 节点内多 CPU,提升热插拔容错。
CPU_SET(0, &cpuset); CPU_SET(1, &cpuset); CPU_SET(2, &cpuset);
  1. 热插拔顺序优化下线 CPU 前先降低其负载,避免瞬时大规模迁移;上线后立即触发 pull_rt_task,快速承接高优任务。

  2. 优先级保护关键 RT 任务优先级≥80,避免被非关键任务抢占;禁用普通任务提升优先级至 RT 范围。

  3. 内核参数调优

sysctl -w kernel.sched_rt_runtime_us=950000 sysctl -w kernel.sched_rt_period_us=1000000

限制 RT 任务总占用,防止系统饿死。

  1. 监控告警持续监控 rt_rq.nr_running、迁移次数、CPU 在线状态,异常时自动触发冗余切换。

七、总结

rq_online 与 rq_offline 是 RT 调度器适配 CPU 热插拔的基石,通过队列状态管理、RT 任务原子推送 / 拉取、优先级位图刷新与根域重均衡,保证 CPU 上下线过程中 RT 任务不丢失、不卡死、调度延迟可控。在实时系统中,CPU 热插拔不再是单纯的节能手段,而是高可用自愈的核心能力。

本文提供的源码分析、测试程序与调试命令可直接用于论文实验、项目验证与线上故障定位。建议开发者结合 trace-cmd 与内核调试深入理解迁移细节,在工业控制、车载、5G 等场景中落地时,务必做长时间压力测试,确保极端场景下实时性与稳定性达标。

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

相关文章:

  • Redis如何利用LFU算法优化缓存命中率
  • D3KeyHelper终极指南:5分钟掌握暗黑3自动化按键助手
  • 你还在为期末课程论文熬夜?好写作AI教你用“三个开关”告别无效忙碌
  • Windows 11任务栏拖放功能终极修复指南:告别系统限制,重获高效工作流
  • 荆州压力型白发养黑理疗馆推荐?黑奥秘毛发慢病管理,头发改善看得见 - 美业信息观察
  • PostgreSQL自动化分区实战:如何用存储过程搞定每日千万级数据表管理
  • 2026现阶段湖南循环水药剂服务商深度**与推荐 - 2026年企业推荐榜
  • 在STM32F407上跑UCOS和emWin?这个示波器项目教你如何分配任务优先级
  • 2026年4月更新:宁波海曙英策企业管理咨询有限公司财务审计服务深度**与口碑解析 - 2026年企业推荐榜
  • 基于合成数据的RAG系统性能优化实践
  • 【Unity ShaderGraph】| 从零搭建你的第一个可视化着色器 | 环境配置 | 核心节点解析 | 实战效果制作
  • Flir Blackfly S多机同步拍摄避坑实录:从帧率减半到曝光异常的解决方案大全
  • 2026年最新吴江松陵婚恋服务机构深度**与**推荐 - 2026年企业推荐榜
  • 2026风管铝箔厂家排行:核心选型维度实测对比 - 优质品牌商家
  • EndNote文献管理:别再手动输入了!一键搞定所有文献类型与缩写
  • 从ADRV9002到ADRV9003:手把手教你移植FPGA驱动,避开那些官方没说的坑
  • 从传感器到ROS Bag:手把手教你搭建一套完整的机器人多传感器数据采集系统
  • JimuReport积木报表:30分钟掌握企业级零代码报表开发终极指南
  • 2026年至今,医用污染袋行业变革下的实力厂商甄选之道 - 2026年企业推荐榜
  • Ubuntu 18.04强制重启后卡在ACPI错误?别急着换内存,试试这三步修复内核
  • 2026年4月上海PMS系统采购指南:如何选择一家靠谱的酒店管理系统服务商 - 2026年企业推荐榜
  • 给RISC-V蜂鸟E203加个‘外挂’:手把手教你用NICE接口实现自定义累加指令
  • 离子阱量子计算中的表面码实现与编译器优化
  • 【实战解析】UE5蓝图通信:从事件分发器到接口,构建高效游戏逻辑
  • 保姆级教程:用Kalibr搞定Realsense D435i三目相机标定(附避坑指南)
  • 2026年q2成都lc7汽车改装机构实测排行:成都,四川越野车轮胎轮毂改装,陆巡汽车改装,优选指南! - 优质品牌商家
  • 2026届最火的降AI率神器实测分析
  • 面试官三连问:什么是大模型的幻觉?产生幻觉的原因是什么?怎么解决?
  • 保姆级教程:用ESP32和MicroPython给ST7735小屏幕做个网络时钟(附完整代码)
  • C#怎么使用Span和Memory C#如何用Span优化内存操作减少GC压力提升性能【进阶】