对齐内存管理?

关于管理对齐的内存块,我有一些相关的问题。 跨平台的答案是理想的。 但是,由于我非常确定不存在跨平台解决方案,因此我主要对Windows和Linux以及(在很大程度上)Mac OS和FreeBSD感兴趣。

  1. 在16字节边界上对齐大块内存的最佳方法是什么? (我知道使用malloc()的简单方法,分配一些额外的空间,然后将指针碰到一个正确对齐的值。虽然我希望能找到一些不那么重要的东西。另外,请参阅以下是其他问题。)

  2. 如果我使用普通的旧malloc() ,分配额外的空间,然后将指针移动到正确对齐的位置,是否有必要将指针保持在块的开头以便释放? (在指向块中间的指针上调用free()似乎在Windows上实际运行,但我想知道标准是什么,即使标准说你不能,它是否在所有主要的实践中都有效操作系统。我不关心模糊的DS9K操作系统。)

  3. 这是一个艰难/有趣的部分 。 在保持对齐的同时重新分配内存块的最佳方法是什么? 理想情况下,这比调用malloc() ,复制,然后在旧块上调用free()更聪明。 我想尽可能在​​适当的地方做。

  1. 如果您的实现具有需要16字节对齐的标准数据类型(例如, long long ),则malloc已经保证您返回的块将正确对齐。 C99状态The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object.第7.20.3节The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object.

  2. 必须将完全相同的地址传递回freemalloc 。 没有例外。 所以是的,你需要保留原始副本。

  3. 如果您已经有一个16字节对齐要求的类型,请参阅上面的(1)。

除此之外,你可能会发现你的malloc实现为了效率而提供了16字节对齐的地址,尽管标准不能保证。 如果需要,您可以随时实现自己的分配器。

我自己,我将在malloc之上实现一个malloc16层,它将使用以下结构:

 some padding for alignment (0-15 bytes) size of padding (1 byte) 16-byte-aligned area 

然后让你的malloc16()函数调用malloc来获得比请求大16个字节的块,找出对齐区域应该在的位置,在此之前放置填充长度并返回对齐区域的地址。

对于free16 ,您只需查看给定地址之前的字节以获取填充长度,从中计算出malloc’ed块的实际地址,并将其传递给free

这是未经测试但应该是一个良好的开端:

 void *malloc16 (size_t s) { unsigned char *p; unsigned char *porig = malloc (s + 0x10); // allocate extra if (porig == NULL) return NULL; // catch out of memory p = (porig + 16) & (~0xf); // insert padding *(p-1) = p - porig; // store padding size return p; } void free16(void *p) { unsigned char *porig = p; // work out original porig = porig - *(porig-1); // by subtracting padding free (porig); // then free that } 

malloc16的魔术线是p = (porig + 16) & (~0xf); 然后将16加到地址然后将低4位设置为0,实际上将它返回到下一个最低对齐点( +16保证它超过了maloc’ed块的实际开始)。

现在,我并不认为上面的代码只是 kludgey。 你必须在感兴趣的平台上测试它,看看它是否可行。 它的主要优点是它抽象了丑陋的一点,所以你永远不必担心它。

  1. 我不知道有任何方式请求malloc返回内存比通常更严格的对齐。 对于Linux上的“通常”,来自man posix_memalign(如果你愿意,你可以使用它而不是malloc()来获得更严格的对齐内存):

    GNU libc malloc()总是返回8字节对齐的内存地址,因此只有在需要更大的对齐值时才需要这些例程。

  2. 必须使用malloc(),posix_memalign()或realloc()返回的相同指针释放()内存。

  3. 像往常一样使用realloc(),包括足够的额外空间,所以如果返回一个尚未对齐的新地址,你可以稍微memmove()它以对齐它。 讨厌,但我能想到的最好。

您可以编写自己的slab分配器来处理对象,它可以使用mmap一次分配页面,维护最近释放的地址缓存以进行快速分配,为您处理所有对齐,并为您提供移动/增长的灵活性对象完全按照您的需要。 malloc非常适合通用分配,但如果您了解数据布局和分配需求,则可以设计一个系统来完全满足这些要求。

最棘手的要求显然是第三个,因为任何基于malloc() / realloc()的解决方案都是realloc()将块移动到不同的对齐方式。

在Linux上,您可以使用使用mmap()而不是malloc()创建的匿名映射。 mmap()返回的地址必须是页面对齐的,并且可以使用mremap()扩展映射。

启动C11,你有void *aligned_alloc( size_t alignment, size_t size ); 基元,参数是:

alignment – 指定对齐方式。 必须是实现支持的有效对齐方式。 size – 要分配的字节数。 对齐的整数倍

返回值

成功时,返回指向新分配内存开头的指针。 必须使用free()或realloc ()释放返回的指针。

失败时,返回空指针。

示例

 #include  #include  int main(void) { int *p1 = malloc(10*sizeof *p1); printf("default-aligned addr: %p\n", (void*)p1); free(p1); int *p2 = aligned_alloc(1024, 1024*sizeof *p2); printf("1024-byte aligned addr: %p\n", (void*)p2); free(p2); } 

可能的输出:

 default-aligned addr: 0x1e40c20 1024-byte aligned addr: 0x1e41000 
  1. 在您的系统上进行实验 在许多系统(尤其是64位系统)上,无论如何都会从malloc()获得16字节对齐的内存。 如果没有,则必须分配额外的空间并移动指针(几乎每台机器上最多8个字节)。

    例如,x86 / 64上的64位Linux有一个16字节long double的双字节,它是16字节对齐的 – 因此所有内存分配都是16字节对齐的。 但是,对于32位程序, sizeof(long double)为8,内存分配仅为8字节对齐。

  2. 是的 – 你只能free() malloc()返回的指针。 其他任何东西都是灾难的秘诀。

  3. 如果您的系统执行16字节对齐分配,则没有问题。 如果没有,那么您将需要自己的重新分配器,它执行16字节对齐分配,然后复制数据 – 或者使用系统realloc()并在必要时调整重新分配的数据。

仔细检查malloc()的手册页; 可能有选项和机制来调整它,使其行为符合您的要求。

在MacOS X上,有posix_memalign()valloc() (它提供页面对齐的分配),并且有一系列由man malloc_zoned_malloc标识的’zoned malloc’函数,标题是

可能能够使用jimmy(在Microsoft VC ++和其他编译器中):

#pragma pack(16)

这样malloc()被强制返回一个16字节对齐的指针。 有点像:

ptr_16byte = malloc( 10 * sizeof( my_16byte_aligned_struct ));

如果它完全适用于malloc(),我认为它也适用于realloc()。

只是一个想法。

– 皮特