临界区


临界资源

一次仅允许一个进程使用的资源称为临界资源,比如一个串口只能同时被一个线程控制收发,如果被其他线程打断了收发的动作,会导致数据不完整。

临界区保护

FreeRTOS提供以下几种临界区保护的方式

taskENTER_CRITICAL

在CM3中底层实现为vPortEnterCritical

void  vPortEnterCritical(  void  )
{
 portDISABLE_INTERRUPTS();
 uxCriticalNesting++;

 \* This is not the interrupt safe version of the enter critical function so
 \* assert() if it is being called from an interrupt context.  Only API
 \* functions that end in "FromISR" can be used in an interrupt.  Only assert if
 \* the critical nesting count is 1 to protect against recursive calls if the
 \* assert function also uses a critical section. */

 if(  uxCriticalNesting  ==  1  )
 {
     configASSERT( ( portNVIC_INT_CTRL_REG &  portVECTACTIVE_MASK  )  ==  0 );
 }
}

portDISABLE_INTERRUPTS() 设置了BASEPRI来屏蔽了所有中断,此时无法进入ISR,也无法进行任务上下文切换。在这之后的所有操作都是绝对安全的。 ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 用来判断当前是否在中断中,因为vPortEnterCritical 并不是中断安全的。在中断中使用的是taskENTER_CRITICAL_FROM_ISR,它会保存之前BASEPRI的值。 uxCriticalNesting是一个计数器,因为临界区是可以嵌套的,它用来记录临界区的嵌套次数。

void  vPortExitCritical(  void  )
{
    configASSERT( uxCriticalNesting );
    uxCriticalNesting--;
  
    if(  uxCriticalNesting  ==  0  )
    {
        portENABLE_INTERRUPTS();
    }
}

vPortExitCritical只会在uxCriticalNesting从1变为0时设置BASEPRI为0来开启中断。

vTaskSuspendAll

是FreeRTOS模拟出来的软件临界区,只能屏蔽上下文切换,不能屏蔽中断。

void  vTaskSuspendAll(  void  )
{
    \* A critical section is not required as the variable is of type
    \* BaseType_t.  Please read Richard Barry's reply in the following link to a
    \* post in the FreeRTOS support forum before reporting this as a bug! -
    \* https://goo.gl/wu4acr */
  
    \* portSOFRWARE_BARRIER() is only implemented for emulated/simulated ports that
    \* do not otherwise exhibit real time behaviour. */
    portSOFTWARE_BARRIER();

    \* The scheduler is suspended if uxSchedulerSuspended is non-zero.  An increment
    \* is used to allow calls to vTaskSuspendAll() to nest. */
    ++uxSchedulerSuspended;

    \* Enforces ordering for ports and optimised compilers that may otherwise place
    \* the above increment elsewhere. */
    portMEMORY_BARRIER();

}

vTaskSuspendAll维护了uxSchedulerSuspended变量来控制上下文切换