【第24期】RTOS中断优先级的红线:SysCall Safe边界

在裸机里,中断优先级随便设,越高越好;在 RTOS 里,如果你想在中断里调用 API(比如发信号、写队列),这个中断的优先级绝不能高于某个“红线”

1. 事故现场:高优先级中断的“越界”

假设你用的 STM32 (Cortex-M),FreeRTOS 配置如下:

  • 红线 (Max SysCall Priority):5

  • 你的串口中断优先级:2 (数字越小,优先级越高,所以 2 比 5 高,也就是 2 更急)

代码逻辑:

void USART_IRQHandler(void) {
    // 这是一个高优先级中断 (2)
    // 试图向队列发送数据
    xQueueSendFromISR(xQueue, &data, &xHigherPriorityTaskWoken); 
}

结果:系统崩溃。 原因:你触碰了 RTOS 的禁区。


2. 为什么会有这条“红线”?

要理解这个问题,得先看 RTOS 的内核在做什么。 RTOS 内核经常需要操作核心链表(比如把任务从“就绪表”移到“延时表”)。这些操作必须是原子的,不能被打断。

在裸机里,我们通过“关总中断”来保护临界区。 但在 RTOS 里,为了保证系统的实时性,RTOS 不会关所有中断,它只关“部分”中断。

  • RTOS 的临界区策略: 当内核操作链表时,它会设置 BASEPRI 寄存器,屏蔽掉优先级 5 及以下(5, 6, 7...15)的所有中断。 此时,优先级 0~4 的中断依然可以发生!

冲突推演

  1. RTOS 内核正在修改任务链表(临界区内),它屏蔽了 5~15 号中断。

  2. 突然,你的串口中断(优先级 2)来了。因为 2 < 5,硬件允许它打断 RTOS 内核。

  3. 你的中断服务函数里调用了 xQueueSendFromISR

  4. 这个 API 内部也会去操作链表(把接收任务叫醒)。

  5. 灾难:RTOS 内核改了一半的链表,被你的中断强行插入又改了一次。链表指针彻底断裂,数据结构损坏。

这就是为什么 “高优先级中断(高于红线)”绝对不能调用 RTOS API


3. 如何划分:两类中断策略

在设计 RTOS 系统时,我们必须把中断分为两个阶级:

阶级一:受管辖中断 (Kernel-Aware Interrupts)
  • 优先级范围低于或等于红线(在 STM32 上通常是 5~15)。

  • 特点

    • 允许调用 RTOS API(必须带 FromISR 后缀)。

    • 有延迟:当 RTOS 进入临界区时,这些中断会被暂时屏蔽(Masked),必须等 RTOS 出来后才能响应。延迟通常极短(几微秒),但存在。

  • 适用场景:99% 的业务中断,如串口接收、按键、普通定时器、I2C/SPI 完成中断。

阶级二:自由中断 (Kernel-Agnostic Interrupts)
  • 优先级范围高于红线(在 STM32 上通常是 0~4)。

  • 特点

    • 严禁调用任何 RTOS API。甚至不能调用 vTaskNotify

    • 零延迟:RTOS 根本管不着它们。无论 RTOS 是否在忙,这些中断都能瞬间响应。

  • 适用场景:极高实时性要求、且不需要和任务交互的逻辑。例如:

    • 电机 PWM 逐波限流保护(炸机保护,必须要快)。

    • 高频 ADC 采样并直接存入 DMA 缓冲区(不经过 RTOS)。


4. 另一个细节:FromISR 后缀的意义

为什么在中断里必须用 xQueueSendFromISR,而不能用普通的 xQueueSend

这不仅仅是名字的区别,而是因为 中断环境 (Context)任务环境 有本质不同:

  1. 不能阻塞: 普通的 xQueueSend 假如遇到队列满了,可以设置等待时间(比如 vTaskDelay(100))。 但在中断里,绝对不能阻塞/睡觉!中断必须快进快出。所以 FromISR 版本没有阻塞时间参数,满了就直接返回失败。

  2. 调度触发方式不同: 普通 API 内部会自动触发任务切换(Context Switch)。 而在中断里,为了效率,API 不会立马切换,而是通过修改 xHigherPriorityTaskWoken 变量,告诉中断退出代码:“喂,刚才有个高优先级任务被我叫醒了,你退出中断的时候记得手动触发一下调度(PendSV)。”


本期核心内容总结

  1. SysCall 红线configMAX_SYSCALL_INTERRUPT_PRIORITY 是 RTOS 安全的边界。

  2. 规则

    • 想调 API?优先级必须 于红线(数值更大)。

    • 想零延迟?优先级设为 于红线(数值更小),但别碰 API。

  3. API 后缀:ISR 里只能用 FromISR 版本的函数,且绝不能阻塞。

/***************************************************
 * 本文为作者《嵌入式开发基础与工程实践》系列文章之一。
 * 关注即可订阅后续内容更新,翻阅往期信息,采用异步推送机制,无需主动轮询。
 * 转发本文可视为一次网络广播,有助于更多节点接收该信息。
 ***************************************************/

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值