当我看一下Atmel软件框架中的一些驱动程序实现时,我遇到了几个使用内存障碍的情况。
障碍定义:
#define barrier() asm volatile("" ::: "memory")
示例1 (中断帮助函数):
static inline void cpu_irq_restore(irqflags_t flags)
{
barrier();
SREG = flags;
}
这个障碍对我有意义。由于cpu_irq_restore
被隐式内联,因此它可以防止从实际函数调用位置重新排序关键(从执行顺序的角度来看)SREG赋值。
作为旁注:SREG
被定义为一个特殊的函数寄存器,定义为:
#define _SFR_MEM8(mem_addr) (*(volatile uint8_t *)(mem_addr))
示例2 (来自AVR TWI驱动程序):
static inline status_code_t twim_release(void)
{
/* First wait for the driver event handler to indicate something
* other than a transfer in-progress, then test the bus interface
* for an Idle bus state.
*/
while (OPERATION_IN_PROGRESS == transfer.status);
while (! twim_idle(transfer.bus)) { barrier(); }
status_code_t const status = transfer.status;
transfer.locked = false;
return status;
}
然而,第二个用例对我来说并不清楚。编译器以什么方式优化该代码,以便在没有barrier()
?
我认为在阅读this article之后,我理解了软件记忆障碍背后的基本原因。
第二个例子中使用屏障背后的原因是什么?
答案 0 :(得分:1)
twim_idle
函数可能会读取与TWI相关的特殊函数寄存器的值并检查其中的一些位。没有障碍,危险在于编译器可能优化该寄存器的读取并将其移动到第二个循环之前。那么第二个循环就像这样:
bool idle = twim_idle(transfer.bus);
while (!idle);
这是一种优化,可以使循环更快,并且对于正常变量的读取是有效的,但在这种情况下显然会导致问题。在循环中设置障碍应该可以防止优化发生。
实际上,您不应该需要内存屏障,因为所有特殊功能寄存器都使用volatile
关键字限定,因此编译器不会尝试缓存对它们的访问。