21

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

  1. 让一块内存在 16 字节边界上对齐的最佳方法是什么?(我知道使用的简单方法malloc(),分配一点额外空间,然后将指针提升到正确对齐的值。不过,我希望能少一些杂乱无章的东西。另外,请参见下文以获取更多信息问题。)

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

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

4

7 回答 7

20
  1. 如果您的实现具有需要 16 字节对齐的标准数据类型(long long例如),malloc则已经保证您返回的块将正确对齐。C99 的第 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. 必须将完全相同的地址传回free给您malloc。没有例外。所以是的,你需要保留原件。

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

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

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

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

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

对于free16,您只需查看给定地址之前的字节以获得填充长度,从中计算出 malloc 块的实际地址,然后将其传递给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
}

中的魔法线malloc16p = (porig + 16) & (~0xf);16 添加到地址,然后将低 4 位设置为 0,实际上将其带回到下一个最低对齐点(+16保证它超过了 macoc'ed 块的实际开始)。

现在,我并没有声称上面的代码是杂乱无章的。您必须在感兴趣的平台上对其进行测试,看看它是否可行。它的主要优点是它抽象了丑陋的部分,因此您永远不必担心它。

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

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

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

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

于 2011-02-21T01:25:41.910 回答
1

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

于 2011-02-21T01:50:24.233 回答
1

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

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

于 2011-02-21T02:15:31.467 回答
1

启动 C11,您有void *aligned_alloc( size_t alignment, size_t size );原语,其中参数为:

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

返回值

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

失败时,返回一个空指针。

示例

#include <stdio.h>
#include <stdlib.h>


    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
于 2017-04-30T22:00:00.510 回答
0
  1. 在您的系统上进行实验。在许多系统(尤其是 64 位系统)上,malloc()无论如何都会得到 16 字节对齐的内存。如果没有,您将不得不分配额外的空间并移动指针(在几乎每台机器上最多移动 8 个字节)。

    例如,x86/64 上的 64 位 Linux 有一个 16-byte long double,它是 16 字节对齐的 - 所以所有内存分配无论如何都是 16 字节对齐的。但是,对于 32 位程序,sizeof(long double)is 8 并且内存分配仅 8 字节对齐。

  2. 是的-您只能free()返回由malloc(). 其他任何事情都是灾难的根源。

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

仔细检查您的手册页malloc();可能有一些选项和机制可以对其进行调整,使其按您的意愿运行。

在 MacOS X 上,有posix_memalign()and valloc()(它给出了一个页面对齐的分配),并且有一系列由man malloc_zoned_malloc标头标识的“分区 malloc”函数<malloc/malloc.h>

于 2011-02-21T01:24:52.973 回答
-1

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

#pragma pack(16)

这样 malloc( ) 被迫返回一个 16 字节对齐的指针。类似于以下内容:

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

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

只是一个想法。

——皮特

于 2011-02-21T01:46:26.033 回答