C++-对齐内存

C++ - Aligning memory

本文关键字:内存 对齐 C++-      更新时间:2023-10-16

我真的很想了解这两行发生了什么

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++分配器而不是更特定于操作系统的专用对齐分配函数(例如POSIX的posix_memalign或C11的aligned_alloc)来分配指向页面对齐内存块的指针,即PAGES页数。

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

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

细节:~是逐位反转的,因此ADDR_MASK(可能类似于4096字节页面的0x00000FFF)变成了0xFFFFF000(翻转所有位)。当&对一个值求值时,只保留两个操作数中设置的位。举个例子:对于一个32位指针,我们假设new给了我们0xDEADBEEF,而PAGE_SIZE是4096。在4095(0xFFF)上添加意味着我们有"0xDEADCEE"。然后,我们用0xFFFFF000屏蔽,它消除了低位,给我们0xDEADC000,即0xDEADBEEF之后的第一个页面对齐地址。new返回的任何非页面对齐地址也会发生同样的情况。

如果该值已经进行了页面对齐,例如0xDEADB000,则在4095/0xFFF上相加会得到0xDEADBFFF(注意0xDEADB中没有任何位发生变化),因此当我们掩码以获得对齐的地址时,我们会再次返回0xDEADB000,因为我们已经进行了页对齐。

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