为什么在这里禁用中断是必要的?

8
static void RadioReleaseSPI(void) {
    __disable_interrupt();
    spiTxRxByteCount &= ~0x0100;
    __enable_interrupt();
}

我知道多个任务可能尝试使用SPI资源。spiTxRxByteCount是一个全局变量,用于跟踪SPI当前是否正在被另一个任务使用。当任务需要SPI时,它可以检查spiTxRxByteCount的状态以查看SPI是否正在使用中。当任务完成对SPI的使用后,它调用此函数并清除位,以指示SPI现在已空闲。但为什么要先禁用中断,然后再重新启用它们呢?只是出于谨慎吗?


如果您有一个RTOS,为什么不使用互斥锁呢? - Martin James
在操作系统中,信号量操作是以这种方式实现的:[http://www.mpi-sws.org/~druschel/courses/os/lectures/proc4.pdf] - Grijesh Chauhan
3个回答

16

&=将进行读取-修改-写入操作,该操作不是原子的。在此期间发生中断改变内容的情况下,你不希望结果将使用错误的值。


10

为保证原子访问,您需要禁用中断。您不希望在读取变量时其他进程访问并可能修改该变量。

来自嵌入式计算简介

原子访问的必要性

想象一下这种情况:运行在8位uC上的前台程序需要检查一个16位变量X。因此,它加载高字节,然后加载低字节(或者相反,顺序无关紧要),然后检查16位值。现在想象一下,有一个关联ISR的中断会修改该16位变量。更进一步假设变量的值在程序执行的某个时间点恰好是0x1234。以下就是可能发生的非常糟糕的事情:

  • 前台加载高字节(0x12)
  • ISR发生,将X修改为0xABCD
  • 前台加载低字节(0xCD)
  • 前台程序看到一个16位值为0x12CD。

问题在于一个被认为是不可分割的数据——我们的变量X,在访问它的过程中实际上被修改了,因为CPU访问变量的指令是可以被分割的。因此,我们对变量X的加载已经被破坏了。您可以看到,变量读取的顺序无关紧要。如果在我们的示例中反转顺序,变量将被错误地读取为0xAB34而不是0x12CD。无论哪种方式,读取的值既不是旧的有效值(0x1234),也不是新的有效值(0xABCD)。

写ISR引用数据也不好。这次假设前台程序为ISR编写了先前的值0x1234,并且需要写入一个新值0xABCD。在这种情况下,VBT如下:

  • 前台存储新的高字节(0xAB)
  • ISR发生,将X读取为0xAB34
  • 前台存储新的低字节(0xCD)

再次,代码(这次是ISR)既没有看到先前有效的值0x1234,也没有看到新的有效值0xABCD,而是看到了无效值0xAB34。

虽然spiTxRxByteCount &= ~0x0100;看起来像C语言中的一条单指令,但实际上它是对CPU的几条指令。在GCC中编译,汇编清单如下:

  57:atomic.c      ****     spiTxRxByteCount &= ~0x0100;
  68                    .loc 1 57 0
  69 004d A1000000      movl    _spiTxRxByteCount, %eax
  69      00
  70 0052 80E4FE        andb    $254, %ah
  71 0055 A3000000      movl    %eax, _spiTxRxByteCount
  71      00

如果在这些指令之间发生中断并修改数据,则您的第一个中断服务程序可能会读取错误的值。因此,在对其进行操作之前需要禁用中断,并声明该变量为volatile


0

禁用中断的原因有两个:

  • &= 是一种读-修改-写操作,本质上不是原子性的。它包括读取、按位与和写入。您不希望这个操作被 ISR(中断服务路由)打断。ISR 可以在读取和写入之间修改 spiTxRxByteCount。然后写入将基于过时的值,导致信息丢失。
  • __disable_interrupt()__enable_interrupt() 作为软件屏障。即使启用了优化,编译器也不能在两个屏障之间移动读取或写入。此外,编译器不能在两个屏障之间缓存 spiTxRxByteCount 的值。如果没有屏障,编译器将允许在多次调用 RadioReleaseSPI() 时跨多个 CPU 寄存器保存 spiTxRxByteCount 的副本。如果启用了内联并重复调用 RadioReleaseSPI(),通常会发生这种情况。

禁用和启用中断作为屏障的作用至少与避免 ISR 中断同样重要,我认为这一点有时被忽视了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接