为什么需要内存对齐?

L. *_*ldt 2 memory x86 assembly memory-alignment low-level

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

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

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

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

2)如果是这样,那么字符串(字符序列)n_chars * 4 字节大吗?我很确定它不是那样的,但是我应该如何解释“将始终阅读其单词边界”呢?

3)内存对齐似乎只与数据结构有关。为什么?内存的其余部分是否未对齐?我的意思是物理、虚拟、内核空间等?

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

我只是很困惑请帮助我

fuz*_*fuz 7

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

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

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

所以现在,对于您的问题:

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

也许。这取决于您使用的硬件。但是,是的,如果您请求一个字节,您将只会得到一个字节。你不应该关心硬件读取多少字节来给你一个字节。

2)如果是这样,那么字符串(字符序列)n_chars * 4 字节大吗?我很确定它不是那样的,但是我应该如何解释“将始终阅读其单词边界”呢?

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

3)内存对齐似乎只与数据结构有关。为什么?内存的其余部分是否未对齐?我的意思是物理、虚拟、内核空间等?

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

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

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

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

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