3. RTOS基础回顾

在 Linux 与 RTOS 多系统协同开发中,Linux 侧通常负责复杂协议栈、文件系统与人机交互,而 RTOS 侧承担硬实时控制、采样与执行动作。若对 RTOS 的任务调度、同步通信、中断与内存机制理解不深,就会出现“Linux 明明发了指令,RTOS 却延迟抖动很大”或“共享数据偶发错乱”的问题。本节把 RTOS 的关键机制串成一条可验证的知识链,为后续核间通信与协同调度打基础。

一、任务调度:优先级、就绪态与抢占

RTOS 调度的核心不是“公平”,而是“可预测”。任务通常处于运行、就绪、阻塞、挂起等状态,调度器在 SysTick 或 PendSV 触发的节拍上选择最高优先级就绪任务运行。以 FreeRTOS 为例,抢占式调度下高优先级任务一旦就绪,可立即打断低优先级任务;同优先级任务可按时间片轮转。关键参数包括任务优先级、栈深度、阻塞时间与节拍频率。

/* FreeRTOS 任务创建示例:优先级与栈深度的选择直接影响实时性 */
xTaskCreate(vControlTask,  "ctrl",  512, NULL, 5, &xCtrlHandle);
xTaskCreate(vCommTask,     "comm",  1024, NULL, 3, &xCommHandle);
xTaskCreate(vLogTask,      "log",   2048, NULL, 1, &xLogHandle);

/* 节拍频率决定时间片粒度,也影响功耗与中断负载 */
#define configTICK_RATE_HZ  1000   /* 1 ms 一个 tick */

优先级分配要遵循“速率单调”思想:周期越短、截止时间越紧的任务优先级越高。若把日志、打印等非实时任务设为高优先级,会直接破坏控制环路的确定性。

二、同步与通信:信号量、互斥量与队列

任务之间需要交换数据或互斥访问外设。二值信号量常用于任务与中断的同步,计数信号量用于资源计数,互斥量(Mutex)带优先级继承,适合保护共享资源,队列则用于传递结构化数据。选择错误会引入优先级反转或数据覆盖。

机制典型用途是否可从中断调用风险点
二值信号量中断通知任务处理是(FromISR)重复 give 导致计数丢失
计数信号量缓冲区槽位计数是计数与真实资源不一致
互斥量保护共享外设/变量否在中断中使用会断言失败
消息队列任务间传递采样值是队列满时阻塞或丢包
事件组多条件同时满足再唤醒是位定义冲突、未清标志
中断服务程序只能使用带 FromISR 后缀的 API,并且退出前应调用 portYIELD_FROM_ISR() 触发一次上下文切换,否则高优先级任务要等到下一个 tick 才被唤醒。

三、中断管理:上半部与下半部

RTOS 中断处理遵循“快进快出”。ISR 中只做清标志、取数据、发信号量等短操作,把耗时处理交给任务(下半部)。中断优先级数值在不同内核中含义相反:Cortex-M 中数值越小优先级越高,且 RTOS 可管理的中断优先级不能高于 configMAX_SYSCALL_INTERRUPT_PRIORITY,否则在 ISR 中调用 RTOS API 会破坏内核临界区。

void EXTI0_IRQHandler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
        EXTI_ClearITPendingBit(EXTI_Line0);
        xSemaphoreGiveFromISR(xAdcSem, &xHigherPriorityTaskWoken);
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
}

常见误区是把串口解析、浮点运算放进 ISR,导致中断响应时间被拉长,Linux 侧看到的通信抖动随之增大。

四、内存管理与实时性指标

RTOS 内存管理强调确定性。动态堆分配(malloc)可能产生碎片和不确定耗时,实时路径上更推荐静态分配、内存池或固定块分配。FreeRTOS 提供 heap_1 到 heap_5 多种策略:heap_1 只分配不释放,heap_4 支持合并相邻空闲块,heap_5 支持多内存区域。

衡量实时性常用指标包括:中断延迟(从中断发生到 ISR 第一条指令)、任务切换时间、最坏执行时间(WCET)、截止时间错过率与抖动(Jitter)。在协同系统中,还要关注跨核通信的端到端延迟。

RTOS 实时性关键路径与指标 中断源 定时器 / GPIO / 串口 ISR 上半部 清标志 / 发信号量 调度器 优先级抢占 / 时间片 任务下半部 控制 / 通信 / 日志 t0 中断发生 t1 ISR 入口 t2 任务切换 t3 任务执行 中断延迟 ISR 执行时间 调度与切换开销 中断延迟 越短越好 受临界区影响 任务切换时间 与栈保存相关 PendSV 完成 WCET 最坏执行时间 决定可调度性 抖动 Jitter 周期波动 影响控制品质 协同要点:Linux 侧下发指令 → 核间通信 → RTOS 中断/任务响应,端到端延迟 = 通信延迟 + 中断延迟 + 调度延迟 + 执行时间 实时路径上避免动态内存、长临界区与高优先级非实时任务

五、案例与复盘:采样控制环路的抖动治理

某设备用 Linux 做界面与网络,RTOS 做 1 kHz 电流环。现场出现电机噪声增大,示波器测得控制周期抖动约 300 μs。排查发现:日志任务优先级被设为 4,与控制任务相同,且日志任务中调用了 printf 与动态内存;同时串口 ISR 中做了整帧解析。整改措施如下:

  1. 日志任务降为优先级 1,并改用环形缓冲区与 DMA 发送。
  2. 串口 ISR 只把字节写入环形缓冲区并 give 信号量,解析移到任务。
  3. 控制任务栈改为静态分配,关闭该任务路径上的 malloc。
  4. 把 configMAX_SYSCALL_INTERRUPT_PRIORITY 与串口中断优先级重新核对,确保 ISR 可安全调用 FromISR API。

整改后抖动降到约 40 μs,满足控制要求。复盘结论是:实时性不是单点优化,而是“中断—调度—同步—内存”全链路的确定性设计。

常见误区:把 RTOS 当成“更小的 Linux”。RTOS 不追求吞吐与公平,而追求可预测;在实时任务中使用动态内存、阻塞式打印、无界队列,都会把确定性变成概率。

掌握以上机制后,后续讨论 Linux 与 RTOS 的核间通信、共享内存与协同调度时,才能准确判断延迟来自哪一层,并给出可测量的优化手段。