临界区(Critical Section)详解
在实时操作系统中,临界区是一段必须原子执行的代码区域,用于保护共享资源(如全局资源、外设寄存器)免受多任务/中断的并发访问破坏。
为什么需要临界区:
当多任务或中断同时访问共享资源时,可能引发:
-
数据竞争:任务A修改一半的数据时被任务B读取
-
非原子操作破坏:如
i++(实际是 读取-修改-写入 三步操作) -
硬件状态不一致:配置外设时被中断打断导致寄存器配置错误
FreeRTOS 临界区实现机制:FreeRTOS通过关闭中断或挂起调度器实现临界区保护
1.任务级临界区(最常用)
// 进入临界区(可嵌套)
taskENTER_CRITICAL();
// 受保护的代码区域
access_shared_resource();
// 退出临界区(需配对使用)
taskEXIT_CRITICAL();
原理:
-
关闭 <= configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断
-
高优先级中断(如硬件故障)仍可触发
-
临界区可嵌套(内部维护计数器)
2. 中断级临界区(ISR中使用)
// 保存当前中断状态
UBaseType_t uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();
// 受保护的代码
hardware_register_access();
// 恢复中断状态
taskEXIT_CRITICAL_FROM_ISR(uxSavedInterruptStatus);
原理:
-
临时屏蔽指定优先级的中断
-
需保存/恢复中断状态以支持嵌套
3. 调度器挂起(长临界区)
vTaskSuspendAll(); // 挂起调度器(任务不切换)
// 长耗时操作(如写Flash)
write_flash_sector(data);
xTaskResumeAll(); // 恢复调度器
适用场景:
-
操作耗时 > 数百微秒
-
不涉及中断共享的资源
-
注意:中断仍可触发,但不会引发任务切换
临界区使用铁律
1.保持极短:
-
理想长度 < 10条指令
-
绝对禁止 在临界区内调用阻塞API(如
vTaskDelay())
// 错误示例!会导致系统崩溃
taskENTER_CRITICAL();
vTaskDelay(100); // 死锁警告!
taskEXIT_CRITICAL();
2.嵌套匹配:
确保 enter/exit严格配对
void safe_operation() {
taskENTER_CRITICAL(); // 嵌套层1
if(condition) {
taskENTER_CRITICAL(); // 嵌套层2
// ...
taskEXIT_CRITICAL(); // 退出层2
}
taskEXIT_CRITICAL(); // 退出层1
}
3.中断优先级规划:
在 FreeRTOSConfig.h中正确设置:
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 5
// 优先级高于5的中断不受临界区影响 会正常执行 不受临界区的限制
临界区 vs 其他同步机制
|
机制 |
中断延迟 |
适用场景 |
性能影响 |
|---|---|---|---|
|
临界区 |
极低 |
极短操作(<5μs) |
暂停部分中断响应 |
|
调度器挂起 |
无 |
较长操作(100μs~1ms) |
暂停所有任务调度 |
|
互斥锁 |
中等 |
复杂资源保护(可阻塞) |
可能引发任务切换 |
|
信号量 |
中等 |
同步/资源计数 |
可能引发任务切换 |
经验法则:
总结
临界区是RTOS中最轻量级且高效的共享资源保护方案,但如同手术刀——用得好能救命,用不好会致命。遵循三条黄金法则:
极短(不超过内存拷贝操作时间)
无阻塞(禁止调用任何可能引发阻塞的API)
精确配对(确保每个enter都有对应的exit)
在实时性要求极高的场景(如电机控制PWM更新),临界区是无可替代的解决方案。
能用临界区解决的不用互斥锁,能用互斥锁解决的不用调度器挂起。
3799

被折叠的 条评论
为什么被折叠?



