对齐内存管理?
关于管理对齐的内存块,我有一些相关的问题。 跨平台的答案是理想的。 但是,由于我非常确定不存在跨平台解决方案,因此我主要对Windows和Linux以及(在很大程度上)Mac OS和FreeBSD感兴趣。
-
在16字节边界上对齐大块内存的最佳方法是什么? (我知道使用
malloc()
的简单方法,分配一些额外的空间,然后将指针碰到一个正确对齐的值。虽然我希望能找到一些不那么重要的东西。另外,请参阅以下是其他问题。) -
如果我使用普通的旧
malloc()
,分配额外的空间,然后将指针移动到正确对齐的位置,是否有必要将指针保持在块的开头以便释放? (在指向块中间的指针上调用free()
似乎在Windows上实际运行,但我想知道标准是什么,即使标准说你不能,它是否在所有主要的实践中都有效操作系统。我不关心模糊的DS9K操作系统。) -
这是一个艰难/有趣的部分 。 在保持对齐的同时重新分配内存块的最佳方法是什么? 理想情况下,这比调用
malloc()
,复制,然后在旧块上调用free()
更聪明。 我想尽可能在适当的地方做。
-
如果您的实现具有需要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.
-
您必须将完全相同的地址传递回
free
的malloc
。 没有例外。 所以是的,你需要保留原始副本。 -
如果您已经有一个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。 你必须在感兴趣的平台上测试它,看看它是否可行。 它的主要优点是它抽象了丑陋的一点,所以你永远不必担心它。
-
我不知道有任何方式请求malloc返回内存比通常更严格的对齐。 对于Linux上的“通常”,来自man posix_memalign(如果你愿意,你可以使用它而不是malloc()来获得更严格的对齐内存):
GNU libc malloc()总是返回8字节对齐的内存地址,因此只有在需要更大的对齐值时才需要这些例程。
-
你必须使用malloc(),posix_memalign()或realloc()返回的相同指针释放()内存。
-
像往常一样使用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
-
在您的系统上进行实验 在许多系统(尤其是64位系统)上,无论如何都会从
malloc()
获得16字节对齐的内存。 如果没有,则必须分配额外的空间并移动指针(几乎每台机器上最多8个字节)。例如,x86 / 64上的64位Linux有一个16字节
long double
的双字节,它是16字节对齐的 – 因此所有内存分配都是16字节对齐的。 但是,对于32位程序,sizeof(long double)
为8,内存分配仅为8字节对齐。 -
是的 – 你只能
free()
malloc()
返回的指针。 其他任何东西都是灾难的秘诀。 -
如果您的系统执行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()。
只是一个想法。
– 皮特