结构与数组的对齐规则冲突

Conflicting alignment rules for structs vs. arrays

如标题所示,问题是关于 x86-64 聚合类型在 Linux 上的对齐。

在我们的讲座中,教授介绍了结构(及其元素)与所附幻灯片的对齐方式。因此,我会假设(根据维基百科和其他讲座 material)对于任何聚合类型,对齐是根据其最大成员。不幸的是,在以前的考试问题中似乎并非如此,其中说:

"Assuming that each page table [4kB, each PTE 64b] is stored in memory at a “naturally aligned” physical address (i.e. an address which is an integer multiple of the size of the table), ..."

为什么对于页面 table(afaik 基本上是内存中的 8 字节值数组),对齐规则不是根据最大元素,而是根据整体的大小 table?

非常感谢澄清!
菲利克斯

为什么页面 table 的大小对齐

对于转换虚拟地址过程中的给定级别,要求当前页面 table 按字节大小对齐可加快索引操作。
CPU无需实际加法即可找到下一级页面的基数table,它可以对索引进行缩放,然后替换当前级基数中的最低位。

你可以通过几个例子说服自己这确实是这种情况。
x86 也遵循这种对齐方式并非巧合。

例如,对于x86的4KiB页面的4级分页CPU,64位地址的页面目录指针字段是9位宽。
table(PDPTE)中的每个条目都是 64 位,因此页面大小为 4096KiB,最后一个条目的偏移量为 511 * 8 = 4088(十六进制为 0xff8,因此最多只使用 12 位)。
页目录指针的地址 table 由 PML4 条目给出,这些条目没有指定基址的低 12 位(用于其他目的),仅指定高位。
CPU 然后可以简单地将 PML4 条目中的低 12 位替换为 PDPTE 的偏移量,因为我们已经看到它的大小为 12 位。

这在硬件中实现速度快且成本低(无进位,使用寄存器很容易实现)。

假设一个国家/地区的邮政编码由两个字段组成:城市代码 (C) 和区块代码 (D),加在一起。
另外,假设给定城市最多可以有 100 个块代码,因此 D 的长度为 2 位数字。
要求城市代码对齐 100(这意味着 C 的最后两位数字为零)使得 C + D 就像用 D 替换 C 的最后两位数字。 (1200 + 34 = 12|34).

与聚合对齐的关系

页面 table 不被视为聚合,即 8 字节元素的数组。它被视为 它自己的 的一种类型,由 CPU 的 ISA 定义并且必须满足 CPU 使用的特定部分的要求它。
页面浏览器发现让页面 table 与其大小对齐很方便,因此这是要求。

聚合的对齐是编译器用来在内存中分配对象的一组规则,它保证满足每个元素的对齐,以便指令可以访问任何元素而无需对齐 penalties/fault.
加载和存储的执行单元是 CPU 与页面漫游器不同的部分,因此需要不同。

您应该使用聚合对齐来了解编译器将如何对齐您的结构然后检查这是否足以满足您的用例

存在例外情况

请注意,教授在解释页面 tables 的自然边界对齐意味着什么方面做了大量工作。
存在例外情况,如果您被告知数据必须在 X 上对齐,您可以假设涉及到一些硬件 trick/simplification 并尝试查看是哪一个,但最后您只是进行对齐并继续前进。

Margaret 解释了为什么页表很特别,我只回答问题的其他部分。


according to the largest element.

这也不是普通结构的规则。你想要 max(alignof(member)) 而不是 max(sizeof(member))。所以 "according to the most-aligned element" 将是描述正常结构所需对齐方式的更好方法。

例如在 i386 System V ABI 中,double 的 sizeof = 8 但 alignof = 4,因此 alignof(struct S1) = 41

即使 char 成员是最后一个,sizeof(struct S1) 仍然需要填充到它的 alignof() 的倍数,所以所有通常的不变量都被保留(例如 sizeof( array ) = N * sizeof(struct S1)),因此按 sizeof 步进总能使您到达新结构开始时充分对齐的边界。


脚注 1:ABI 是在 CPU 能够高效地一次 load/store 8 个字节之前设计的。现代编译器尝试提供 double 和 [u]int64_t 8 字节对齐,例如作为结构之外的全局变量或局部变量。但是 ABI 的结构布局规则根据任何 doubleint64_t 对象的 minimum 保证对齐来修复布局,对于这些类型是 alignof(T) = 4 .

x86-64 System V 对所有原始类型都有 alignof(T) = sizeof(T) ,包括 8 字节的。这使得对任何正确对齐的 int64_t 的原子操作成为可能,例如,简化 C++20 std::atomic_ref 的实现而不必检查对齐是否充分。 ()