从裸机到 RTOS 的临界区陷阱:为什么 __disable_irq() 在 FreeRTOS 下会破坏调度器

· 3 浏览

回答(4)

如果确实需要极短全局关中断,可用 taskENTER_CRITICAL_FROM_ISR 的变体或直接操作 BASEPRI,但必须保证退出时恢复原值,且不能嵌套调用阻塞 API。
RTOS搬砖工 · 2026-10-07
实测踩坑:在临界区里调用 printf 或浮点运算,关中断时间过长,SysTick 计数丢失,任务延时直接漂移几十毫秒,建议临界区只做寄存器级操作。
CortexM小白 · 2026-10-07
关键区别是 BASEPRI 与 PRIMASK:__disable_irq 操作 PRIMASK 关掉所有可屏蔽中断,而 FreeRTOS 临界区用 BASEPRI 只屏蔽低优先级中断,SysTick/PendSV 仍能触发。
嵌入式老油条 · 2026-10-07
在裸机里,__disable_irq() 只是关掉全局中断,代码执行完再开,逻辑简单可控。但在 FreeRTOS 下,调度器本身依赖 SysTick 和 PendSV 中断来切换任务,而 __disable_irq() 会把这些中断一并屏蔽。如果你在临界区里调用了会阻塞的 API(如 vTaskDelay、xQueueReceive),任务无法被切换,系统直接死锁;即使不阻塞,长时间关中断也会让 SysTick 丢失,导致时间片统计和延时精度崩坏。正确做法是使用 FreeRTOS 提供的 taskENTER_CRITICAL()/taskEXIT_CRITICAL(),它们通过 BASEPRI 只屏蔽低于 configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断,保留高优先级中断和调度器节拍。若必须用底层指令,应使用 portSET_INTERRUPT_MASK_FROM_ISR() 或 vPortRaiseBASEPRI(),并严格配对、尽量短小。记住:临界区不是关中断,而是“只关不该被打断的那部分”。
mcuku 阿沐 · 2026-10-07