防止嵌套调用

时间:2013-01-29 11:56:19

标签: c embedded interrupt c99

我有一个禁用中断的功能,但问题是如果我禁用它们并且我调用一个也禁用/启用它们的功能,它们会过早地重新启用。以下逻辑是否足以阻止这种情况?

static volatile int IrqCounter = 0;

void EnableIRQ()
{
    if(IrqCounter > 0)
    {
        IrqCounter--;
    }

    if(IrqCounter == 0)
    {
        __enable_irq();
    }
}

void DisableIRQ()
{
    if(IrqCounter == 0)
    {
        __disable_irq();
    }

    IrqCounter++;
}

4 个答案:

答案 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++;
}