在实时操作系统(RTOS)中,确保任务或中断服务例程(ISR)访问共享资源时的正确性和一致性是非常重要的。进入临界区和开关中断是实现这一目标常用的两种技术,它们的目的相似,但实现方式和适用场景有所不同。
进入临界区
临界区是指一个访问共享资源(如全局变量、硬件资源等)的程序代码段,这部分代码在任意时刻只能由一个任务或中断服务例程执行。进入临界区的目的是防止多任务或多中断在访问共享资源时发生冲突。
在RTOS中,进入临界区通常通过以下步骤实现:
- 禁止任务切换:在进入临界区之前,系统会禁止任务切换,这样可以防止在执行临界区代码时被其他任务打断。
- 执行临界区代码:在确保没有其他任务可以执行的情况下,执行访问共享资源的代码。
- 允许任务切换:执行完临界区代码后,重新允许任务切换。
进入临界区并不关闭中断,它只是阻止了其他任务的执行。在中断仍然开启的情况下,中断服务例程仍然可以执行,这意味着中断服务例程也需要考虑临界区的保护。
开关中断
开关中断是指在中断服务例程或任务中暂时关闭中断,以保护临界区代码不被中断。
开关中断的步骤如下:
- 关闭中断:在访问共享资源之前关闭中断,确保在执行临界区代码时不会被任何中断打断。
- 执行临界区代码:在关闭中断的情况下安全地执行访问共享资源的代码。
- 打开中断:执行完临界区代码后,重新开启中断。
区别
-
适用范围:
- 进入临界区通常用于多任务环境中,确保任务间的同步和互斥。
- 开关中断用于确保在执行某些重要代码时不会被任何中断打断,适用于中断服务例程和任务。
-
执行效率:
- 进入临界区可能只涉及任务调度器的控制,而不一定涉及硬件层面的中断控制,效率可能更高。
- 开关中断涉及硬件层面的操作,频繁地开关中断可能会影响系统的响应性和中断延迟。
-
资源保护:
- 进入临界区通常是为了保护任务间的共享资源。
- 关闭中断是为了保护代码段,防止被中断服务例程打断。
-
实现复杂性:
- 进入临界区的实现通常依赖于RTOS提供的API。
- 开关中断直接与硬件操作相关,需要谨慎使用,以避免系统错误。
/* 假设我们有一个共享资源,这里是一个全局变量 */
volatile int sharedResource = 0;
void functionThatAccessesSharedResource(int value)
{
/* 进入临界区,关闭中断 */
vPortEnterCritical();
/* 在这里执行对共享资源的操作 */
sharedResource = value; /* 写入共享资源 */
// int temp = sharedResource; /* 读取共享资源 */
/* 离开临界区,恢复中断 */
vPortExitCritical();
/* 可以在这里使用 temp 变量进行其他操作 */
}
分析portENTER_CRITICAL和portEXIT_CRITICAL的代码:
void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS();
uxCriticalNesting++;
if( uxCriticalNesting == 1 )
{
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
}
}
void vPortExitCritical( void )
{
configASSERT( uxCriticalNesting );
uxCriticalNesting--;
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS();
}
}
进入临界段和退出临界段是通过函数调用开关中断函数 portENABLE_INTERRUPTS 和 portDISABLE_INTERRUPTS 实现的。