11

我知道某些 CPU 架构不支持未对齐的地址访问(例如,ARM 4 之前的 ARM 架构没有访问内存中半字对象的指令)。并且该架构的某些编译器(例如,某些版本的 GCC)在发现未对齐的地址时会使用一系列内存访问,因此未对齐的访问对开发人员几乎是透明的。(请参阅The Definitive Guide to GCC,作者 William冯哈根

但我想知道编译器如何知道地址是否对齐?毕竟,编译器看到的是虚拟地址(有效地址,EA),如果它可以看到任何东西的话。当程序运行时,EA 可以被操作系统映射到任何物理地址。即使虚拟地址对齐,生成的物理地址也可能未对齐,不是吗?物理地址的对齐是真正重要的,并且在 CPU 地址线上传输。

因为编译器根本不知道物理地址,它怎么能聪明到知道变量的地址是否对齐呢?

4

2 回答 2

16

虚拟地址不只是映射到任何物理地址。虚拟内存位于以对齐方式映射到物理页面的页面中。(通常与 4096 对齐)。

请参阅:虚拟内存和对齐 - 它们如何组合在一起?

于 2012-12-19T13:53:36.700 回答
2

对齐对于目标代码来说是一个非常有用的属性,部分原因是一些机器坚持“对齐访问”,但在现代计算机中,因为缓存行对性能有巨大影响,因此代码/循环/数据/锁的缓存对齐是一个要求您本地友好的编译器。

世界上几乎所有的加载器都支持在大小适中的 2 次方对齐边界处加载代码,甚至更大。(汇编器和链接器也通过各种 ALIGNMENT 指令支持这一点)。通常,链接器和加载器只是将第一个加载的值与众所周知的边界大小对齐;具有虚拟内存的操作系统通常根据 VM 页面大小提供方便的边界(与其他答案相关)。

因此,编译器基本上可以知道其发出的代码/数据的对齐方式是什么。通过跟踪它发出了多少代码,它可以知道任何发出的值的对齐方式。如果它需要对齐,它可以发出链接器指令,或者对于适度的大小,只需填充直到发出的代码量适当对齐。

正因为如此,您可以非常确定大多数编译器不会将代码或数据构造放置在跨越缓存线(或其他架构强加)边界的方式中,这种方式会严重影响性能,除非有指示这样做。

于 2012-12-19T14:57:00.767 回答