【问题标题】:What does posix_memalign/memalign doposix_memalign/memalign 做什么
【发布时间】:2019-02-27 13:23:00
【问题描述】:

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

有人可以帮我了解它的工作原理和用途吗?或者,也许提供一个使用示例?

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

【问题讨论】:

    标签: c memory memory-pool


    【解决方案1】:

    malloc 为您提供了一块可以进行任何对齐的内存(唯一的要求是它必须与实现支持的最大原始类型对齐),posix_memalign 为您提供一块内存保证有要求的对齐方式。

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

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

    【讨论】:

    • malloc 的结果不只是“任何”对齐。它保证可以针对系统上的任何本机类型(int、long、double、structs 等)进行适当对齐。您是正确的,较大的对齐可能用于特殊目的,但对于绝大多数应用程序来说,malloc 的结果肯定对齐得很好。
    • 是否有充分的理由选择 posix_memalign 而不是分配对齐内存的 mmap
    • @EliBendersky posix_memalign 通常会从堆中分配一个块,而mmap 将始终分配给操作系统。例如,如果您想分配许多缓存行对齐的内存块,那么posix_memalign 是首选。与mmap 相比,更喜欢malloc 的原因是相同的。
    【解决方案2】:

    malloc 总是返回设置为任何原始类型所需的最大对齐的内存。这允许malloc'd 内存存储您可能需要的任何类型。我对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;
    }
    

    【讨论】:

    • 谢谢!作为 memalign 的替代品,非常有用的一段代码,在 mingw 中仍然缺少。
    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

      它的工作原理取决于实现。该函数的目的是为您提供一个 n 字节对齐的内存块(块的起始地址是 n 的乘积)。

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          当你在 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*) 的倍数。

          【讨论】:

            猜你喜欢
            • 2011-09-18
            • 2016-02-15
            • 2022-06-10
            • 2011-03-23
            • 2014-11-12
            • 2011-09-18
            • 2014-04-29
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多