内存地址对齐

Posted

技术标签:

【中文标题】内存地址对齐【英文标题】:Memory Address Alignment 【发布时间】:2012-04-18 14:49:28 【问题描述】:

我对内存对齐的概念有点困惑。所以这是我的疑问: 文本说的是,如果你想读取 4 个字节的数据,从一个不能被 4 整除的地址开始,你就会遇到内存访问未对齐的情况。例如,如果我想从地址 05 开始读取 10 个字节,这将被称为未对齐访问 (http://www.mjmwired.net/kernel/Documentation/unaligned-memory-access.txt)。

这种情况是否特定于 4 字节字可寻址架构,或者这对字节可寻址架构也有效?如果上述情况对于字节可寻址架构是不对齐的,为什么会这样?

谢谢!

【问题讨论】:

这是操作系统用来解决的问题之一...... 据我所知,没有一个处理器具有 10 字节数据类型。让我们使用具有 32 位数据总线并试图从地址 1 读取 32 位值的普通处理器保持简单。这需要首先读取地址 0 处的值,将字节 1 移动到偏移量 0,移动字节 2到偏移量 1,将字节 3 移动到偏移量 2。然后读取地址 4 处的值并将字节 0 移动到偏移量 3。听起来涉及,不是吗:) 【参考方案1】:

作为一般规则,内存中的第 0 位被选通到总线上,并且该总线的第 0 位连接到每个寄存器的第 0 位。这样一直持续到第 31 位。可能有特殊的硬件将每个字节(第 15:8、23:16 和 31:24 位)引导到低位字节,即第 7:0 位。 (当你到达位“32”时,它实际上是地址 4 的 4 字节字的位 0。)

但是,在标称情况下,没有任何特殊硬件可以将字节移动到除了按自然顺序标称连接到的位置之外的任何位置,并且可能是字节通道 0。

想象一个具有 32 个数据引脚的简单内存芯片和一个具有 32 个数据引脚的简单 CPU。每个芯片上的给定数据引脚连接到另一个芯片上的相应数据引脚,并且仅连接到那个数据引脚。简单的 CPU 根本 根本无法 进行未对齐的读取操作。

因此,考虑从 0 读取。接下来的 4 个字节全部落入已连接的寄存器中,从地址 4 读取也会发生这种情况。但是如果您从地址 1 读取(32 位)怎么办?还是2?还是3?虽然读取不能直接在硬件中完成,但一个花哨的控制器可以导致很多事情发生:

CPU 可以进行两次读取以获取所有位。它不能同时做,它只有 32 个引脚。一次读取来自地址 0,一次读取来自地址 4 然后,CPU 必须执行各种移位、屏蔽和异或运算,以便从这两个组件中构造一个单词。

所有这些都需要额外的时间。


注意。实际上,数据总线通常是 32 位的倍数,存储器也是如此。可能存在用于重新对齐对象的特殊硬件。但即便如此,因为这是一种异常情况,它可能无法获得正确对齐读取所获得的管道优化,即使使用特殊硬件,通过它运行操作数也可能会花费时间。

【讨论】:

【参考方案2】:

对齐与数据大小和寻址有关。大多数指令集/软件的寻址以字节为单位。 0,1,2,3 都是有效的字节地址。假设您正在访问的内存系统或外围设备是“字节可寻址”的,基本上您可以向其写入单个字节,您通常有允许您使用任何地址值的指令。当你有超过一个字节时开始对齐,两个字节,如果对齐意味着地址的lsbit为零,未对齐意味着它是一。四个字节,32 位数量,低两位为零,对齐,一个或两个不为零,不对齐,依此类推。可以把它想象成你想要一个模 4 = 0 在 4 字节边界上对齐的地址。

现在,通常作为一名软件工程师,您不会故意将自己置于需要在地址 5 处获取 10 个字节的情况下,您可能会在 0x4 处获取 12 个字节或在 0x0 处获取 16 个字节或类似的东西,即使您只使用其中的 10 个,您会更合乎逻辑地对齐它们。外部影响、网络数据包、文件系统、共享内存、硬件等,任何时候你跨越一个编译域,你都可能不得不处理这个问题并采取相应的行动。 10 个字节是半有趣的,这取决于您是尝试将这些字节复制到另一个同样糟糕的地址,还是只是读取它们或写入它们。如果阅读,您可能只想在地址 0x4 处读取 12 个字节并完成它。如果写得好,您可以在一个漂亮的循环中完成所有 10 个或一次展开一个字节,您可以在 0x5 写入一个,在 0x6 写入两个,在 0x8 写入四个,在 0xC 写入两个,在 0xE 写入一个,或者在 0x5 写入一个,循环或展开 4 个 16 位值,从 0x6 开始,然后在 0xE 处有一个字节。等等。

既然您说读取,您可以读取 0x4 处的 3 个 32 位量或从 0x0 开始的两个 64 位量。这在很大程度上取决于您打算如何处理数据以及您使用的指令集等。10 字节读取的循环可能是最干净/最简单的读取、维护等。

如果您想知道对齐与未对齐,那么就像我上面提到的那样,您可以做一个

8 bit access at 0x5
16 bit access at 0x6
32 bit access at 0x8
16 bit access at 0xC
8 bit access at 0xE

正如我一直说的那样,对于可能不是最有效的读取。对于写入,您可以读取修改写入 32 位或 64 位数量或我上面提到的组合。

【讨论】:

以上是关于内存地址对齐的主要内容,如果未能解决你的问题,请参考以下文章

C语言精要总结-内存地址对齐与struct大小判断篇

内存编址寻址内存对齐

如何检查内存地址在 C 中是不是为 32 位对齐

内存对齐问题

Sword 计算机内存对齐

内存对齐与页面对齐