ARM中的临界区

11

我有在AVR处理器家族上实现关键段的经验,在这种情况下,您只需要禁用中断(当然要使用内存屏障),执行关键操作,然后重新启用中断:

void my_critical_function()
{
   cli();  //Disable interrupts
   // Mission critical code here
   sei();  //Enable interrupts
}

现在我的问题是:

这种简单的方法是否也适用于ARM处理器架构?我听说过处理器预先查找指令和其他黑科技,主要想知道这些类型的东西是否会对这个关键部分的实现造成问题。

2个回答

17
Assuming you're on a Cortex-M processor, take a look at the LDREX and STREX instructions, which are available in C via the __LDREXW() and __STREXW() macros provided by CMSIS (the Cortex Microcontroller Software Interface Standard). They can be used to build extremely lightweight mutual exclusion mechanisms.

Basically,


如果您使用的是Cortex-M处理器,请查看CMSIS提供的__LDREXW()__STREXW()宏,以通过LDREXSTREX指令构建非常轻量级的互斥机制。
data = __LDREXW(address)

它的工作方式类似于data = *address,但它在CPU中设置了“独占访问标志”。当您完成操作数据后,请使用它进行写回。

success = __STREXW(address, data)

这个函数类似于 *address = data,但只有在独占访问标志仍然设置的情况下才能成功写入。如果它成功写入,则还会清除该标志。它返回0表示成功,返回1表示失败。如果 STREX 失败,则必须返回到 LDREX 并重试。

对于共享变量的简单独占访问,不需要做其他处理。例如:

do {
  data = LDREX(address);
  data++;
} while (STREXW(address, data));
这个机制有趣的地方在于它实际上是“后来居上”的;如果这段代码被中断,且中断使用了LDREXSTREX,那么STREX中断将会成功,而低优先级的用户代码将不得不重试。
如果您正在使用操作系统,则可以使用相同的基元构建“适当的”信号量和互斥量(例如,请参见此应用说明);但是如果您正在使用操作系统,则可能已经通过其API访问了互斥量!

1
很酷......我正在使用C++(使用模板包装器)创建自己的原子数据类型,我可以将其提供为另一个后端,以便我可以混合和匹配实现..... - DarthRubik
很酷。它们替换了旧的32位ARM指令集中的SWP指令,该指令执行原子加载/存储(类似的功能在其他平台上也可用)。但是,LDREX/STREX对内存总线更加友好,这无论如何都是好事,但如果您有多个处理器共享总线,则开始变得非常重要。在我看来,这是一个巧妙的设计。 - cooperised
1
使用ldrex和strex指令来保护关键部分的方法是有效的,也是最好的方法。但是,如果您想通过禁用和重新启用中断来保护关键部分,则应在禁用中断之前添加内存屏障指令。该指令告诉CPU从禁用中断的点停止乱序执行。如果没有屏障指令,代码可能无法按预期运行。这仅适用于像Cortex-M这样为了更好的性能而重新排序指令的处理器。 - SRK

4
ARM架构非常广泛,根据我的理解您可能是指ARM Cortex M微控制器。您可以使用这种技术,但许多ARM微控制器提供更多功能。由于我不知道实际的硬件,所以只能给您一些例子:
1. 位带区域。在这个内存区域中,您可以以原子方式设置和重置位。 2. 硬件信号量(STM32H7) 3. 硬件MUTEX(一些NXP微控制器)
等等。

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