给定一个运行以下代码的微控制器:
volatile bool has_flag = false;
void interrupt(void) //called when an interrupt is received
{
clear_interrupt_flag(); //clear interrupt flag
has_flag = true; //signal that we have an interrupt to process
}
int main()
{
while(1)
{
if(has_flag) //if we had an interrupt
{
has_flag = false; //clear the interrupt flag
process(); //process the interrupt
}
else
sleep(); //place the micro to sleep
}
}
假设if(has_flag)
条件评估为假,我们即将执行睡眠指令。 在我们执行sleep指令之前,我们收到一个中断。离开中断后,我们执行睡眠指令。
这个执行顺序是不可取的,因为:
- 微控制器进入睡眠状态,而不是醒来并调用
process()
。 - 如果此后没有接收到中断,微控制器可能永远不会唤醒。
- 调用
process()
被推迟到下一次中断。
如何编写代码来防止这种竞争条件的发生?
编辑
一些微控制器,例如 ATMega,有一个睡眠使能位,可以防止这种情况发生(感谢 Kvegaoro 指出这一点)。JRoberts 提供了一个示例实现来说明这种行为。
其他的micro,比如PIC18s,没有这个位,问题依旧。但是,这些微控制器的设计使得无论是否设置了全局中断使能位,中断仍然可以唤醒内核(感谢 supercat 指出这一点)。对于此类架构,解决方案是在进入睡眠状态之前禁用全局中断。如果在执行睡眠指令之前触发了中断,则不会执行中断处理程序,内核将被唤醒,并且一旦重新启用全局中断,将执行中断处理程序。在伪代码中,实现如下所示:
int main()
{
while(1)
{
//clear global interrupt enable bit.
//if the flag tested below is not set, then we enter
//sleep with the global interrupt bit cleared, which is
//the intended behavior.
disable_global_interrupts();
if(has_flag) //if we had an interrupt
{
has_flag = false; //clear the interrupt flag
enable_global_interrupts(); //set global interrupt enable bit.
process(); //process the interrupt
}
else
sleep(); //place the micro to sleep
}
}