当我的 CPU 不支持未对齐的内存访问时,这意味着什么?



我刚刚发现我正在编写代码的ARM(Cortex M0)不支持未对齐的内存访问。

现在在我的代码中,我使用了很多打包结构,我从来没有收到任何警告或硬错误,那么当 Cortex 不允许未对齐的访问时,它怎么能访问这些结构的成员呢?

像 gcc 这样的编译器了解对齐,并将发出正确的指令来解决对齐问题。 如果你有一个打包的结构,你会告诉编译器,这样它就会提前知道如何进行对齐。

假设您使用的是 32 位架构,但有一个像这样打包的struct

struct foo __attribute__((packed)) {
   unsigned char bar;
   int baz;
}

当访问baz时,它将在 32 位边界上执行内存加载,并将所有位移动到位。

在这种情况下,它可能会在地址 bar 处加载 32 位,在地址 bar + 4 处加载 32 位。 然后,它将应用一系列逻辑运算,例如移位和逻辑或/和,以在 32 位寄存器中获得正确的 baz 值。

查看程序集输出以了解其工作原理。 您会注意到,在这些体系结构上,未对齐的访问将低于对齐的访问的效率。

在许多较旧的8位微处理器上,有加载(和存储)大于内存总线宽度的寄存器的指令。 这种操作将通过从一个地址加载一半的寄存器,另一半从下一个更高的地址加载来执行。 即使在内存总线宽于 8 位(例如 16 位)的系统上,将内存视为可寻址的字节集合通常也很有用。 从任何地址加载字节将导致处理器读取 16 位内存位置的一半并忽略另一半。 从偶数地址读取 16 位值将导致处理器读取整个 16 位内存位置并使用整个内存;该值将与读取两个连续的字节地址并连接结果相同,但它将在一次操作中读取,而不是两次。

在某些这样的系统上,如果尝试从奇数地址读取 16 位值,处理器将使用一个值的一半和另一个值的另一半读取两个连续的地址,就像一个人执行了两次单字节读取并合并结果一样。 这称为未对齐的内存访问。 在其他系统上,这样的操作将导致总线故障,这通常会触发某种形式的中断,该中断可能会也可能不会对此做一些有用的事情。 支持未对齐访问的硬件相当复杂,设计代码以避免未对齐的访问通常不太困难。 因此,此类硬件通常只存在于已经非常复杂的处理器上,或者运行代码的处理器是为从单字节读取组装多字节寄存器的处理器设计的(例如,在 8088 上,每 16 位读取需要两次 8 位内存提取,并且许多 8088 代码在后来的英特尔处理器上运行)。

相关内容

最新更新