【发布时间】:2010-07-28 04:06:18
【问题描述】:
我知道如何在 x86 ASM 中原子地写入一个值。但是我怎么读呢? LOCK 前缀不能与 mov 一起使用。
为了增加价值,我在做:
lock inc dword ptr Counter
如何以线程安全的方式读取 Counter?
【问题讨论】:
标签: assembly x86 thread-safety atomic
我知道如何在 x86 ASM 中原子地写入一个值。但是我怎么读呢? LOCK 前缀不能与 mov 一起使用。
为了增加价值,我在做:
lock inc dword ptr Counter
如何以线程安全的方式读取 Counter?
【问题讨论】:
标签: assembly x86 thread-safety atomic
正如我在this 帖子中向您解释的那样:
对可缓存内存的访问 跨越总线宽度、缓存线、 和页面边界不保证 由 Intel Core 2 Duo 实现原子化, 英特尔酷睿双核、奔腾 M、奔腾 4、 Intel Xeon、P6 系列、Pentium 和 Intel486 处理器。英特尔酷睿 2 双核、英特尔酷睿双核、奔腾 M、 Pentium 4、Intel Xeon 和 P6 系列 处理器提供总线控制信号 允许外部存储器子系统 使拆分访问原子化; 然而,非对齐的数据访问将 严重影响性能 处理器,应该避免使用。
所以使用:
LOCK CMPXCHG EAX, [J]
LOCK CMPXCHG 首先锁定缓存,然后将 EAX 与目标值进行比较,如果目标值不相等,则 EAX 中的结果为目标值。
编辑: 链接到:
Intel® 64 and IA-32 Architectures Software Developer’s Manuals
在Volume 3A: System Programming Guide 中查看第 8.1.1 节
同时检查:Optimization Reference Manual section: CHAPTER 7 OPTIMIZING CACHE USAGE
【讨论】:
mov 加载/存储是原子的 if it doesn't cross an 8-byte boundary (for cached accesses). 或者对于未缓存的,如果它是对齐的或不跨越双字边界的 16 位访问。 [J] 也只是一个绝对或(在 x86-64 中)RIP-relative 寻址模式。这不是双重间接。它组装得很好。 MASM 语法通常会省略 [],但它们在 MASM 中是可选的,在 NASM 中是必需的。
lock cmpxchg 进行负载而被否决。 在静态数据上,只需在J: 标签前使用ALIGN 4。
我不是汇编专家,但字长(在 x86 上,32 位)读/写应该已经是原子的了。
您需要锁定增量的原因是因为它既是读取又是写入。
【讨论】:
mov 如果地址对齐,则加载/存储是原子的。 x86 有连贯的缓存,所以即使多个 CPU 正在读/写同一个位置,you won't get tearing unless the value is misaligned.
对于简单的阅读,主要是关于对齐。确保原子读取的最简单方法是始终使用“自然”对齐 - 即对齐至少与项目的大小一样大(例如,32 位项目是 32 位对齐的)。
未对齐的读取不一定是原子的。举个极端的例子,考虑在奇数地址读取一个 32 位值,其中第一个字节在一个高速缓存行中,而其他三个字节在另一个高速缓存行中。在这种情况下,原子读取基本上是不可能的。
由于(至少大多数)处理器使用 64 位宽的内存总线,因此希望以原子方式读取的最大项目是 64 位。
【讨论】:
阅读其他回复很有趣。我认为@GJ 可能正在赚钱。
多年来,32 位读写是原子的,这一直是事实。直到最近几年,真正激进的缓存才不再保证这一点。
我想这就是为什么在我和机器代码之间我更喜欢 C++、Java 或其他一些东西的原因。如今,机器代码过于复杂而无法可靠编写(除非您大量这样做以保持您的技能敏锐)。幸运的是,今天的优化编译器非常好,您很少需要手动优化的汇编器的性能。
【讨论】: