为什么在x86上对自然对齐的变量进行整数赋值?

时间:2016-04-14 13:38:39

标签: c++ c concurrency x86 atomic

我一直在阅读关于原子操作的article,并且它提到了x86上32位整数赋值是原子的,只要该变量是自然对齐的。

为什么自然对齐可以确保原子性?

4 个答案:

答案 0 :(得分:6)

如果一个32位或更小的对象在" normal"中自然对齐。作为内存的一部分,它可以用于除了之外的任何80386或兼容处理器 80386sx在一次操作中读取或写入对象的所有32位。虽然平台以快速有用的方式做某事的能力并不一定意味着平台有时会因某种原因以某种其他方式做到这一点,而我相信它是可能的在很多(如果不是所有)x86处理器上都有内存区域,一次只能访问8位或16位,我不认为英特尔曾经定义过任何要求对#32进行对齐32位访问的条件34;正常"记忆区域会导致系统读取或写入部分值而不会读取或写入整个内容,而且我也不认为英特尔有任何意图定义任何此类内容,以及#34; normal"记忆的领域。

答案 1 :(得分:2)

自然对齐意味着类型的地址是类型大小的倍数。

例如,一个字节可以在任何地址,一个短(假设16位)必须是2的倍数,一个int(假设32位)必须是4的倍数和一个长(假设为64) bits)必须是8的倍数。

如果您访问一个非自然对齐的数据,CPU将引发故障或读取/写入内存,但不会作为原子操作。 CPU采取的操作取决于架构。

例如,我们已经获得了下面的内存布局:

01234567
...XXXX.

int *data = (int*)3;

当我们尝试读取*data时,组成该值的字节分布在2个int大小的块中,1个字节在块0-3中,3个字节在块4-7中。现在,仅仅因为块在逻辑上彼此相邻,它并不意味着它们是物理的。例如,块0-3可以位于cpu缓存行的末尾,而块3-7位于页面文件中。当cpu进入访问块3-7以获得它需要的3个字节时,它可能会看到该块不在内存中并且表示它需要内存被分页。这可能会阻止调用进程操作系统将内存重新打入。

在内存被分页之后,但是在你的进程被唤醒之前,可能会有另一个进程写入Y到地址4.然后你的进程被重新安排并且CPU完成了读取,但现在它已经读取了XYXX,而不是您期望的XXXX。

答案 2 :(得分:1)

如果你问的是它的设计原因,我会说它是CPU架构设计的好产品。

回到486时代,没有多核CPU或QPI链接,因此原子性当时并不是一个严格的要求(DMA可能需要它吗?)。

在x86上,数据宽度为32位(或x86_64为64位),这意味着CPU可以一次性读写数据宽度。并且存储器数据总线通常与该数量相同或更宽。结合对齐地址的读/写是一次完成的事实,自然没有什么能阻止读/写是非原子的。你可以同时获得速度/原子。

答案 3 :(得分:0)

要回答您的第一个问题,如果变量存在于其大小的倍数的内存地址,则该变量自然会对齐。

如果我们只考虑 - 你链接的文章 - 赋值指令,那么对齐保证原子性,因为MOV(赋值指令)在对齐数据上是设计原子的。

其他类型的指令,例如INC,需要 LOCK ed(x86前缀,在前缀操作期间,它允许对当前处理器的共享内存进行独占访问),即使数据是对齐的,因为它们实际上是通过多个步骤(=指令,即load,inc,store)执行的。