为什么需要内存对齐?

Why is memory alignment needed?

我知道这个问题已经被问过一千次了,我已经通读了每一个答案,但我还是不明白。可能我的 RAM 模型存在一些基本错误,导致我无法理解任何答案。

我从互联网上得到了所有这些小信息,但我就是无法连接它们。

以下是我目前想知道的:以 IA-32 架构为例,其字边界为 32 位(边界 = CPU 可以从内存中读取的最大值?)。它总是读入它的字边界。

1) 那么,无论我给它什么地址,它总是读取 4 个字节?如果我在地址 x 处有一个简单的字符怎么办?它会从该地址读取 4 个字节,然后做一些奇怪的事情只得到一个字节吗?

2)如果是这样,那么一个字符串(一个char的序列)n_chars * 4字节大吗?我很确定不是那样,但我应该如何解释 "will always read its word boundary" 呢?

3) 内存对齐好像只有Data structures才想出来。为什么?内存的其余部分是否未对齐?我的意思是物理、虚拟、内核 Space 等?

4) 为什么我只能在可被 4 整除的地址存储 32 位值?我的意思是我知道它最终只会读取 32 位,但为什么它不能从奇数地址读取 32 位?比如这里有什么限制?

我很困惑,请帮助我

在现代计算机中,内存是字节导向的。每个字节都有自己的地址,可以单独从 RAM 中获取。为了你的程序,你可以假设获取一个单词的行为就像获取以任意顺序组成它的字节,然后将它们组装成你加载到的寄存器中的一个单词。

请注意,这是一种抽象。内存芯片通常以一次获取 8 个或更多字节的方式连接。 CPU 有一些电路可以将所有这些从机器代码中抽象出来。但是,这种抽象是 leaky,它会导致一些影响:

  • 如果数据未与其对齐要求对齐,则内存访问可能需要额外的周期,因为数据跨越的字数超过了必要的数量。通过充分对齐数据可以避免这种惩罚。
  • 在获取或写入对齐数据时,这会转化为硬件中的单个获取或存储。这样的获取或存储是atomic,这是并发代码中的重要属性。取或写未对齐数据时,需要多次取或存,操作不再是原子的。
  • 有些CPU根本不支持reading/writing未对齐内存,因为这简化了电路设计。这种限制在现代硬件中变得越来越少见。

那么现在,对于您的问题:

1) So, whatever address I give it, it will always read 4 bytes? What if I have a simple char at address x. Will it read 4 bytes from that address and then do something weird to only get the one byte?

也许吧。这取决于您使用的硬件。但是,是的,如果您请求一个字节,您将只能得到一个字节。您不应该关心硬件读取了多少字节才能为您提供一个字节。

2) If that is so, then is a string (a sequence of char) n_chars * 4 Bytes big? I'm pretty sure it isn't that way, but how am I supposed to interpret "will always read its word boundary" then?

一个字符串通常是 n_chars 字节大。当您从字符串中读取一个字符时,您会得到一个字节。硬件可能会读取更多字节来满足您的请求,但这不是您需要关心的事情。请注意 Windows 有时使用每个字符占用两个字节的 UTF-16 字符串,但这种趋势并没有真正流行起来。

3) Memory alignment seems to only come up with Data structures. Why? Is Memory unaligned in the rest of the memory? And I mean for Physical, Virtual, Kernel Space etc?

无论何时考虑 RAM 中的数据,内存对齐都很重要。该内存是否在内核或您的用户进程中使用并不重要。 MMU 通常以保持对齐的方式映射内存,因此无论您使用物理内存还是虚拟内存都没有关系。磁盘上的数据没有这些对齐要求,但由于您使用的存储扇区大小,其他性能特征可能适用。

4) Why can I store a 32 Bit value only at addresses dividable by 4? I mean I get that it will eventually read only 32 bits, but why can it not read 32 bits from an odd address? Like what is the restriction here?

如果您从奇地址读取 32 位,根据您的 CPU 和操作系统,会发生以下情况之一:

  1. 很管用
  2. 可以用,但有点慢
  3. CPU 默默地忽略低 2 位,而是从相应的对齐地址读取(现在很少见)
  4. CPU 抛出一个异常,如果你不处理它会使你的程序崩溃
  5. CPU抛出一个异常,操作系统会捕获该异常来为您模拟内存访问。

您通常不应该假设其中的哪一种会发生。永远不要编写读取未对齐数据的代码。如果您需要读取未对齐的数据,请考虑单独读取每个字节,然后手动将字节重新组合成您想要的数据。