posix_memalign / memalign做了什么

时间:2011-07-03 13:20:46

标签: c memory memory-pool

我正在尝试了解memalign()posix_memalign()的功能。阅读可用的文档没有帮助。

有人可以帮我理解它是如何工作的以及它用于什么?或者,也许提供一个用法示例?

我正在尝试了解linux内存的工作原理,我需要编写自己的简单内存池(低碎片堆)。

8 个答案:

答案 0 :(得分:57)

尽管malloc为您提供了一块可以进行任何对齐的内存(唯一的要求是它必须针对实现支持的最大基元类型进行对齐),posix_memalign会为您提供一个块保证具有请求对齐的内存。

所以结果例如posix_memalign(&p, 32, 128)将是一个128字节的内存块,其起始地址保证为32的倍数。

这对于需要遵循特定对齐的内存的各种低级操作(例如使用SSE指令或DMA)非常有用。

答案 1 :(得分:10)

malloc始终返回设置为任何基元类型所需的最大对齐的内存。这允许malloc'内存存储您可能需要的任何类型。我对posix_memalign的描述的理解是,它返回一个内存位置,其地址将是您指定的任何对齐的倍数。

我不确定在编写自定义内存池时这会有多大用处,但我已经开始提供一个如何实现它的示例。不同之处在于我的示例,分配了malloc_aligned的任何内容都必须使用free_aligned释放;但是,使用posix_memalign,您可以使用free

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

void *malloc_aligned(size_t alignment, size_t bytes)
{
    // we need to allocate enough storage for the requested bytes, some 
    // book-keeping (to store the location returned by malloc) and some extra
    // padding to allow us to find an aligned byte.  im not entirely sure if 
    // 2 * alignment is enough here, its just a guess.
    const size_t total_size = bytes + (2 * alignment) + sizeof(size_t);

    // use malloc to allocate the memory.
    char *data = malloc(sizeof(char) * total_size);

    if (data)
    {
        // store the original start of the malloc'd data.
        const void * const data_start = data;

        // dedicate enough space to the book-keeping.
        data += sizeof(size_t);

        // find a memory location with correct alignment.  the alignment minus 
        // the remainder of this mod operation is how many bytes forward we need 
        // to move to find an aligned byte.
        const size_t offset = alignment - (((size_t)data) % alignment);

        // set data to the aligned memory.
        data += offset;

        // write the book-keeping.
        size_t *book_keeping = (size_t*)(data - sizeof(size_t));
        *book_keeping = (size_t)data_start;
    }

    return data;
}

void free_aligned(void *raw_data)
{
    if (raw_data)
    {
        char *data = raw_data;

        // we have to assume this memory was allocated with malloc_aligned.  
        // this means the sizeof(size_t) bytes before data are the book-keeping 
        // which points to the location we need to pass to free.
        data -= sizeof(size_t);

        // set data to the location stored in book-keeping.
        data = (char*)(*((size_t*)data));

        // free the memory.
        free(data);
    }
}

int main()
{
    char *ptr = malloc_aligned(7, 100);

    printf("is 5 byte aligned = %s\n", (((size_t)ptr) % 5) ? "no" : "yes");
    printf("is 7 byte aligned = %s\n", (((size_t)ptr) % 7) ? "no" : "yes");

    free_aligned(ptr);

    return 0;
}

答案 2 :(得分:8)

除了Oli的回答,我想指出一个更重要的问题。

在最近的x86架构中,缓存行是64字节,它是从内存到缓存的最小数据量。假设您的结构大小为56个字节,那么您有大量的结构。当您查找一个元素时,CPU将需要发出2个内存请求(即使它位于缓存行的中间,它也可能发出2个请求)。这对性能不利,因为您必须等待内存,并且使用更多缓存,这最终会提供更高的缓存未命中率。在这种情况下,仅使用posix_memalign是不够的,但是您应该将结构填充或压缩为64字节边界。

拥有40字节结构只是运气不好:)

答案 3 :(得分:4)

它是如何工作的依赖于实现。该函数的目的是为您提供一个n字节对齐的内存块(块的起始地址是n的乘法)。

答案 4 :(得分:3)

由于memalign已经过时(参考:手册页),因此这里仅描述malloc()和posix_memalign()之间的区别。 malloc()是8字节对齐的(例如,对于RHEL 32位),但对于posix_memalign(),对齐是用户可定义的。要知道这个的用法,也许一个很好的例子是使用mprotect()设置内存属性。要使用mprotect(),内存指针必须是PAGE对齐的。因此,如果使用pagesize作为对齐调用posix_memalign(),则返回的指针可以轻松提交到mprotect()以设置读写可执行属性。 (例如,将数据复制到内存指针后,可以将其设置为只读属性以防止其被修改)。 &#34; malloc()&#34;&#39;返回的指针不能在这里使用。

答案 5 :(得分:0)

GNU C 中使用 posix_memalign 时,应注意第二个参数不仅应为2的幂,而且应为sizeof(void *)。请注意,此要求与memalign函数中的要求不同,后者仅需要2的幂。

int
__posix_memalign (void **memptr, size_t alignment, size_t size)
{
  void *mem;

  /* Test whether the SIZE argument is valid.  It must be a power of
     two multiple of sizeof (void *).  */
  if (alignment % sizeof (void *) != 0
      || !powerof2 (alignment / sizeof (void *))
      || alignment == 0)
    return EINVAL;


  void *address = RETURN_ADDRESS (0);
  mem = _mid_memalign (alignment, size, address);

  if (mem != NULL)
    {
      *memptr = mem;
      return 0;
    }

  return ENOMEM;
}
weak_alias (__posix_memalign, posix_memalign) 

当我们查看posix_memalign实现中的第一个if条件时,它将检查对齐是否为sizeof(void *)的倍数。

答案 6 :(得分:0)

便于在Linux虚拟内存子系统中使用THP:https://youtu.be/fgC6RUlkQE4?t=4930

答案 7 :(得分:-4)

deffault malloc返回指针是8的倍数,这意味着malloc拆分内存到块有8个字节,并在每个块的开头检查空闲内存。有两个问题面孔。

更大的块会浪费更多内存,但更大的块可以帮助C更快地找到免费的块内存。 memalign可以改变这些块的大小。如果要节省内存,请将块的大小减小到2或4.如果要使应用程序更快,请将块的大小增加到2的幂。