在 RTOS(实时操作系统)中,临界区(Critical Section)是指一段需要以原子性方式执行的代码。这意味着在执行临界区代码时,不允许其他任务或中断干扰,以确保数据一致性和操作的正确性。
临界区的必要性
在 RTOS 中,任务是并发运行的,尤其是在多任务环境中,可能会发生以下情况:
- 任务切换(上下文切换):在访问共享资源时,另一个任务可能会抢占当前任务。
- 中断触发:中断服务程序可能会在任务执行时访问共享资源。
- 数据竞争问题:多个任务或中断同时访问同一资源时,可能会引发数据损坏或错误操作。
临界区的作用就是防止以上问题,通过保护共享资源或关键代码段,确保并发环境下的正确性。
定义临界区
临界区通常是指一段需要访问共享资源(如全局变量、硬件寄存器、内存等)的代码。以下是一些典型的例子:
- 修改全局变量。
- 操作队列、信号量、互斥锁等共享资源。
- 访问外部硬件设备寄存器。
1 | int shared_resource = 0; |
如何实现临界区
在 RTOS 中,实现临界区主要在两个方面:
禁止任务间切换
在临界区代码执行期间,暂停任务调度,防止任务之间的切换。这种方法适用于不涉及中断的代码。
- 在 FreeRTOS 中使用:
- taskENTER_CRITICAL() 和 taskEXIT_CRITICAL(): 禁止调度器,防止任务切换,保护临界区代码。
1 | taskENTER_CRITICAL(); |
如果所使用的 FreeRTOS 移植未使用 configMAX_SYSCALL_INTERRUPT_PRIORITY 内核配置常量 (也称为 configMAX_API_CALL_INTERRUPT_PRIORITY),则调用 taskENTER_CRITICAL() 会全局禁用中断。如果所使用的 FreeRTOS 移植使用了 configMAX_SYSCALL_INTERRUPT_PRIORITY 内核配置常量,则调用 taskENTER_CRITICAL() 会禁用优先级等于或低于 configMAX_SYSCALL_INTERRUPT_PRIORITY 设置的优先级的中断, 并启用所有高于此优先级的中断。
禁止中断
在临界区代码执行期间,关闭中断,防止中断访问共享资源。
- 在 FreeRTOS 中使用:
- taskENTER_CRITICAL_FROM_ISR() 和 taskEXIT_CRITICAL_FROM_ISR(): 完全禁用中断,这种方法适合保护非常短的关键代码段,但会导致系统延迟处理其他中断。
1 | uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR(); |
临界区必须尽量简短,否则会对本来可以嵌套的 高优先级中断的响应时间产生不利影响。 每次调用 taskENTER_CRITICAL_FROM_ISR() 时, 都必须有对应的 taskEXIT_CRITICAL_FROM_ISR() 调用。
不得从临界区调用 FreeRTOS API 函数。
如果使用的 FreeRTOS 移植支持中断嵌套,则调用 taskENTER_CRITICAL_FROM_ISR() 将禁用优先级等于或低于由configMAX_SYSCALL_INTERRUPT_PRIORITY(或 configMAX_API_CALL_INTERRUPT_PRIORITY)内核配置常量设置的优先级的中断, 同时启用所有其他高于此优先级的中断。 如果使用的 FreeRTOS 移植不支持中断嵌套,则 taskENTER_CRITICAL_FROM_ISR() 和 taskEXIT_CRITICAL_FROM_ISR() 将不起作用。