Cortex M3 支持一对有用的操作(在许多其他机器中也很常见),称为“Load-Exclusive”(LDREX)和“Store-Exclusive”(STREX)。从概念上讲,LDREX 操作执行加载,还设置了一些特殊的硬件来观察加载的位置是否可能被其他东西写入。对最后一个 LDREX 使用的地址执行 STREX 将导致该地址仅在没有其他内容先写入时才被写入。如果存储发生,则 STREX 指令将使用 0 加载寄存器,如果中止,则将加载 1。
请注意,STREX 通常是悲观的。在多种情况下,即使相关位置实际上没有被触及,它也可能决定不执行存储。例如,LDREX 和 STREX 之间的中断将导致 STREX 假设正在监视的位置可能已被击中。出于这个原因,最好尽量减少 LDREX 和 STREX 之间的代码量。例如,考虑如下内容:
内联无效安全增量(uint32_t *addr)
{
uint32_t 新值;
做
{
新值 = __ldrex(addr) + 1;
} while(__strex(new_value, addr));
}
编译为:
; 假设 R0 保存有问题的地址;r1 被丢弃
LP:
ldrex r1,[r0]
添加 r1,r1,#1
strex r1,r1,[r0]
cmp r1,#0 ; 测试是否非零
bne lp
.. 代码继续
在代码执行的大部分时间里,LDREX 和 STREX 之间不会发生任何事情来“干扰”它们,因此 STREX 将毫不费力地成功。但是,如果在 LDREX 或 ADD 指令之后立即发生中断,则 STREX 将不会执行存储,而是代码将返回读取 [r0] 的(可能更新的)值并计算新的递增值基于此。
使用 LDREX/STREX 来形成像 safe_increment 这样的操作不仅可以管理关键部分,而且在许多情况下还可以避免对它们的需要。