RV32A 有两种类型的原子操作:

  • 内存原子操作(AMO)
  • 加载保留/条件存储(load reserved / store conditional)

原子操作

AMO 指令对内存中的操作数执行一个原子操作,并将目标寄存器设置为操作前的内存值。原子表示内存读写之间的过程不会被打断,内存值也不会被其它处理器修改。

加载保留和条件存储保证了它们两条指令之间的操作的原子性。

  • 加载保留读取一个内存字,存入目标寄存器中,并留下这个字的保留记录。
  • 而如果条件存储的目标地址上存在保留记录,它就把字存入这个地址。
    • 如果存入成功,它向目标寄存器中写入 0;
    • 否则写入一个非 0 的错误代码。

为什么 RV32A 要提供两种原子操作呢?因为实际中存在两种不同的使用场景。 编程语言的开发者会假定体系结构提供了原子的比较-交换(compare-and-swap)操作: 比较一个寄存器中的值和另一个寄存器中的内存地址指向的值,如果它们相等,将第三个寄存器中的值和内存中的值进行交换。这是一条通用的同步原语,其它的同步操作可以以它为基础来完成 [Herlihy 1991]。 尽管将这样一条指令加入 ISA 看起来十分有必要,它在一条指令中却需要 3 个源寄存器和 1 个目标寄存器。源操作数从两个增加到三个,会使得整数数据通路、控制逻辑和指令格式都变得复杂许多。(RV32FD 的多路加法(multiply-add)指令有三个源操作数,但它影响的是浮点数据通路,而不是整数数据通路。)

不过,加载保留和条件存储只需要两个源寄存器,用它们可以实现原子的比较交换(见图 6.3 的上半部分):

另外还提供 AMO 指令的原因是,它们在多处理器系统中拥有比加载保留/条件存储更好的可扩展性,例如可以用它们来实现高效的归约。AMO 指令在于 I/O 设备通信时也很有用,可以实现总线事务的原子读写。这种原子性可以简化设备驱动,并提高 I/O 性能。图 6.3 的下半部分展示了如何使用原子交换实现临界区。

内存一致性模型

RISC-V 具有宽松的内存一致性模型(relaxed memory consistency model),因此其他线程看到的内存访问可以是乱序的。

图 6.2 中,所有的 RV32A 指令都有一个请求位(aq)和一个 释放位(rl)。

  • aq 被置位的原子指令保证其它线程在随后的内存访问中看到顺序的 AMO 操作;
  • rl 被置位的原子指令保证其它线程在此之前看到顺序的原子操作。

想要了解更详细的有关知识,可以查看 [Adve and Gharachorloo 1996]

有什么不同之处? 原始的 MIPS-32 没有同步机制,设计者在后来的 MIPS ISA 中加入了加载保留/条件存储指令。