临界区
临界资源
一次仅允许一个进程使用的资源称为临界资源,比如一个串口只能同时被一个线程控制收发,如果被其他线程打断了收发的动作,会导致数据不完整。
临界区保护
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变量来控制上下文切换