摘要为了保护共享数据用了taskENTER_CRITICAL()结果系统卡死了或者发现关了中断后串口丢数据变少了系统反而变快了不是中断没用而是临界区关断了不该关的中断破坏了系统的实时性。本文解析临界区的正确打开方式。一、问题描述现象**在任务里调用 taskENTER_CRITICAL() 保护链表操作运行一段时间后系统不再响应定时器或按键调试发现程序卡在临界区里出不来了。**很多工程师的直觉是临界区太长忘了调用taskEXIT_CRITICAL()中断里也用了临界区二、原理分析1. 物理模型FreeRTOS 的临界区是通过关中断Disable IRQ​ 实现的。taskENTER_CRITICAL() - 关闭全局中断 - 操作共享数据 - taskEXIT_CRITICAL() - 恢复全局中断2. 核心参数BASEPRIARM Cortex-M 的优先级掩码寄存器FreeRTOS 用它来关中断。中断嵌套高优先级中断打断低优先级中断。3. 反直觉真相“关中断”不等于“关 RTOS 调度”。taskENTER_CRITICAL()关的是中断不是任务调度。如果你在临界区里调用了阻塞函数如vTaskDelay、xQueueSend系统试图切换任务但中断关了切换失败。结果系统锁死。为什么关中断会变快因为中断不再抢占任务CPU 全力跑你的代码但这牺牲了实时性。三、工程级解决方案方案 1临界区里绝对不能有阻塞铁律这是 RTOS 编程的第一禁忌。// 错误示范临界区里有阻塞 taskENTER_CRITICAL(); Do_Something(); vTaskDelay(1); // 绝对禁止会导致死机 taskEXIT_CRITICAL(); // 正确示范快进快出 taskENTER_CRITICAL(); list_add_node(list, node); // 极快的操作 taskEXIT_CRITICAL();方案 2使用调度锁代替中断锁如果你只是不想被任务切换打扰但不想关中断vTaskSuspendAll(); // 挂起调度器中断仍在工作 Do_Something(); xTaskResumeAll(); // 恢复调度器方案 3区分 ISR 临界区和任务临界区任务中用taskENTER_CRITICAL()关中断。ISR 中用portSET_INTERRUPT_MASK_FROM_ISR()关中断并返回掩码。四、选型避坑建议中断里不要用 taskENTER_CRITICALISR 里应该用BaseType_t mask taskENTER_CRITICAL_FROM_ISR();。临界区时间 1us临界区应该只做指针操作或赋值耗时不能超过 1 微秒。不要递归进入临界区虽然 FreeRTOS 支持计数但递归进入极易导致忘记退出。五、总结 Checklist[ ] 临界区里是否有vTaskDelay或其他阻塞调用[ ] 临界区的代码是否足够短 1us[ ] 是否用vTaskSuspendAll()代替了关中断[ ] ISR 中是否使用了正确的FROM_ISR宏六、写在最后关注我少走弯路我是 gqqsherry一个拒绝调包、专注底层逻辑的嵌入式工程师。临界区是 RTOS 里的“核武器”用好了是保护伞用不好就是毁灭者。记住能不用临界区就不用能用调度锁就不用中断锁。关注我的专栏《嵌入式底层避坑指南》RTOS 底层避坑指南系列正式完结。原创文章转载请注明出处。