在 FreeRTOS 中,**临界区(Critical Section)**是一段特殊的代码区域,在这段代码执行期间,系统会暂时屏蔽中断或禁止任务调度,以确保代码的原子性(即不会被其他任务或中断打断)。临界区的主要作用是保护共享资源,防止数据竞争和不一致。


1. 临界区的概念

1.1 定义

临界区是通过暂时屏蔽中断或禁止任务调度来保护共享资源的一段代码。在 FreeRTOS 中,临界区通常通过以下两个函数实现:

  • taskENTER_CRITICAL():进入临界区,屏蔽中断或提升任务优先级。

  • taskEXIT_CRITICAL():退出临界区,恢复中断或任务调度。

1.2 实现原理

  • 屏蔽中断:
    在进入临界区时,FreeRTOS 会暂时屏蔽所有可屏蔽的中断(通常是优先级低于某个阈值的中断)。

  • 禁止任务调度:
    在某些实现中,FreeRTOS 可能会暂时禁止任务调度,确保当前任务不会被其他任务抢占。


2. 什么时候开启和关闭临界区

2.1 开启临界区

在以下情况下需要开启临界区:

  • 访问共享资源时:
    当多个任务或任务与中断共享资源(如全局变量、硬件寄存器)时,需要进入临界区以确保资源的独占访问。

  • 执行原子操作时:
    当需要确保一段代码的原子性(即不会被中断或其他任务打断)时,需要进入临界区。

2.2 关闭临界区

在以下情况下需要关闭临界区:

  • 共享资源访问完成后:
    当对共享资源的操作完成后,应立即退出临界区,以恢复中断和任务调度。

  • 原子操作完成后:
    当需要恢复系统的正常中断响应和任务调度时,应退出临界区。


3. 什么情况下需要使用临界区

3.1 保护共享资源

  • 全局变量:
    当多个任务或任务与中断共享全局变量时,需要使用临界区保护变量的读写操作。

  • 硬件寄存器:
    当多个任务或中断访问硬件寄存器时,需要使用临界区确保操作的原子性。

3.2 执行原子操作

  • 标志位更新:
    当需要更新某个标志位时,可能需要确保操作的原子性。

  • 链表操作:
    当对链表进行插入、删除等操作时,需要使用临界区保护。

3.3 避免数据竞争

  • 多任务竞争:
    当多个任务同时访问共享资源时,可能会导致数据竞争,需要使用临界区保护。

  • 任务与中断竞争:
    当任务和中断同时访问共享资源时,可能会导致数据不一致,需要使用临界区保护。


4. 临界区的作用

4.1 确保原子性

临界区的主要作用是确保一段代码的原子性,即在这段代码执行期间不会被中断或其他任务打断。

4.2 保护共享资源

通过屏蔽中断或禁止任务调度,临界区可以确保共享资源的独占访问,避免数据竞争和不一致。

4.3 提高代码可靠性

临界区可以防止由于多任务或中断竞争导致的不可预测行为,提高代码的可靠性和稳定性。


5. 临界区的使用示例

5.1 保护全局变量

int sharedVariable = 0;

void TaskFunction(void *pvParameters) {
    while (1) {
        // 进入临界区
        taskENTER_CRITICAL();

        // 修改共享变量
        sharedVariable++;

        // 退出临界区
        taskEXIT_CRITICAL();
    }
}

5.2 保护硬件寄存器

void ConfigureHardware() {
    // 进入临界区
    taskENTER_CRITICAL();

    // 配置硬件寄存器
    HW_REGISTER = 0x1234;

    // 退出临界区
    taskEXIT_CRITICAL();
}

5.3 保护链表操作

void AddToList(List_t *list, Node_t *node) {
    // 进入临界区
    taskENTER_CRITICAL();

    // 插入节点
    node->next = list->head;
    list->head = node;

    // 退出临界区
    taskEXIT_CRITICAL();
}

6. 临界区的注意事项

6.1 临界区应尽量短小

  • 临界区会屏蔽中断或禁止任务调度,长时间占用临界区会影响系统的实时性。

  • 应尽量将临界区的代码限制在最小范围内,只保护必要的操作。

6.2 避免嵌套临界区

  • 如果嵌套使用临界区,可能会导致死锁或其他不可预测的行为。

  • 如果需要嵌套,应确保嵌套的顺序和退出顺序一致。

6.3 临界区与互斥量的区别

  • 临界区:屏蔽中断或禁止任务调度,适用于短小操作。

  • 互斥量:基于任务调度,适用于较长时间的操作。


7. 总结

7.1 临界区的核心作用

  • 确保代码的原子性。

  • 保护共享资源,避免数据竞争。

7.2 使用场景

  • 访问共享资源(如全局变量、硬件寄存器)。

  • 执行原子操作(如标志位更新、链表操作)。

7.3 注意事项

  • 临界区应尽量短小。

  • 避免嵌套使用临界区。

  • 在共享资源访问完成后,应及时退出临界区。

通过合理使用临界区,可以显著提高代码的可靠性和稳定性,同时避免多任务和中断竞争导致的问题。

更多推荐