我有一个禁用中断的功能,但问题是如果我禁用它们并且我调用一个也禁用/启用它们的功能,它们会过早地重新启用。以下逻辑是否足以阻止这种情况?
static volatile int IrqCounter = 0;
void EnableIRQ()
{
if(IrqCounter > 0)
{
IrqCounter--;
}
if(IrqCounter == 0)
{
__enable_irq();
}
}
void DisableIRQ()
{
if(IrqCounter == 0)
{
__disable_irq();
}
IrqCounter++;
}
答案 0 :(得分:5)
我所知道的每个操作系统的方式是将IRQ状态保存到本地变量中,然后恢复它。
显然,您的代码有TOCTOU个问题 - 如果两个线程同时运行,请检查IrqCounter> 0,如果IrqCounter == 1,则第一个线程将其视为1,第二个线程将其视为1,并且两者都递减计数器。
我肯定会尝试安排这样的事情:
int irq_state = irq_save();
irq_disable();
... do stuff with IRQ's turned off ...
irq_restore(irq_state);
现在,您不必担心可能会失去同步的计数器等。
答案 1 :(得分:2)
假设您已经拥有一个系统,当中断被禁用时您无法更改上下文,那么您所拥有的内容很好,假设您仔细跟踪何时调用enable() 。
在您在下面的评论中描述的用法中,您计划在中断服务例程中使用这些部分。您的主要用途是阻止高优先级中断运行ISR的某个部分。
请注意,您必须考虑这些嵌套ISR的堆栈深度,因为在从中断返回之前启用中断时,您将在ISR中启用中断。
关于其他答案:enable()缺乏线程安全性(由于if(IrqCounter > 0)
)并不重要,因为任何时候你在enable()上下文切换中都是由于中断被关闭已经被禁用。 (除非出于某种原因,您拥有无与伦比的禁用/启用对,并且在这种情况下您还有其他问题。)
我唯一的建议是将ASSERT添加到启用而不是运行时检查,因为你永远不应该启用你没有禁用的中断。
void EnableIRQ()
{
ASSERT(IrqCounter != 0) //should never be 0, or we'd have an unmatched enable/disable pair
IrqCounter--; //doesn't matter that this isn't thread safe, as the enable is always called with interrupts disabled.
if(IrqCounter == 0)
{
__enable_irq();
}
}
我更喜欢您在save(); disable(); restore();
技术中列出的技术,因为我不想跟踪操作系统的某些部分'每次我处理中断时的数据。但是,您必须知道何时(直接或间接)从ISR调用enable()。
答案 2 :(得分:0)
看起来很好,除了它不是线程安全的。
另一个常见选项是查询中断启用/禁用状态并将其保存到本地变量中,然后禁用中断,然后在禁用中断时执行您想要执行的任何操作,然后从本地变量恢复状态。
答案 3 :(得分:0)
static volatile int IrqCounter = 0;
void EnableIRQ(void)
{
ASSERT(IrqCounter != 0) //should never be 0, or we'd have an unmatched enable/disable pair
if (IrqCounter > 0)
{
IrqCounter--;
}
if (IrqCounter == 0)
{
__enable_irq();
}
}
void DisableIRQ(void)
{
__disable_irq(); // Fix TOCTOU issues. In CMSIS there is no harm in extra disables, so always disable.
IrqCounter++;
}