C++ - 对齐内存

C++ - Aligning memory

我很想了解这两条线上发生了什么

const int PAGES = 8 * 1024;

// PAGES + extra 4KiB for alignment
uint8_t * mem = new uint8_t [ PAGES * CCPU::PAGE_SIZE + CCPU::PAGE_SIZE ];

// align to a mutiple of 4KiB
uint8_t * memAligned = (uint8_t *) (( ((uintptr_t) mem) + CCPU::PAGE_SIZE - 1) & ~(uintptr_t) ~CCPU::ADDR_MASK );

尤其是最后一行,我什么都不懂...

它正在分配指向页面对齐内存块的指针,即使用 C++ 分配器而不是更多 OS 特定专用对齐分配函数(例如 POSIX's posix_memalign or C11's aligned_alloc 页数 PAGES ).

首先,它分配 PAGES + 1 内存页(可能是页对齐的,也可能不是页对齐的),然后向前调整结果指针,使其指向结果中第一个页对齐的字节。通过过度分配一个额外的页面,它知道它肯定会有足够大的分配来拥有超过该点的 PAGES 个可用页面。该程序只需要确保它在完成时 deletes mem,而不是 memAligned(删除后者可能会导致程序现在崩溃,稍后由于堆损坏,或者只是泄漏内存;这是未定义的行为,因此将您的计算机熔化为渣是合法行为。

最后一行在数值上等同于四舍五入到页面大小的下一个倍数;它将 PAGE_SIZE - 1 添加到指针(因此如果指针已经页对齐,它仍然在同一页中,否则它被移动到下一页),然后屏蔽掉地址的低位(这会撤消添加在 "already page aligned" 情况下,以及在所有其他情况下,将指针重置为指向 mem 中未对齐指针之后的第一页的开头。

细节:~是按位反转,所以ADDR_MASK对于4096字节的页面可能类似于0x00000FFF,变成0xFFFFF000(翻转所有位).当 &-ing 一个值时,只保留两个操作数中设置的位。举个例子:对于 32 位指针,我们假设 new 给了我们 0xDEADBEEF,而 PAGE_SIZE 是 4096。加上 4095 (0xFFF) 意味着我们有 ' 0xDEADCEE'。然后我们用 0xFFFFF000 屏蔽,它消除了低位,给我们 0xDEADC0000xDEADBEEF 之后的第一页对齐地址。 new.

返回的任何非页面对齐地址都会发生同样的事情

如果该值已经页面对齐,例如 0xDEADB000,添加 4095/0xFFF 得到我们 0xDEADBFFF(请注意 0xDEADB 中的位没有改变), 所以当我们屏蔽以获得对齐的地址时,我们再次返回 0xDEADB000,因为我们已经是页面对齐的。

转换为 uintptr_t 是为了确保我们可以使用数学运算符操作地址,并确保按位取反填充匹配指针所需的所有位(如果大小不合适,您可以取反,然后上转换,突然间你的左边会有一堆零,而不仅仅是右边,你最终会屏蔽掉指针中的重要位,所以它指向了一个完全不同和错误的地方。