对对齐malloc实现的说明

fhity93d  于 2023-02-21  发布在  其他
关注(0)|答案(4)|浏览(174)

这不是作业,这纯粹是为了我自己的个人教育。
我不知道如何实现一个对齐的malloc,所以在网上找到了this website。为了便于阅读,我将发布下面的代码:

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

void* aligned_malloc(size_t required_bytes, size_t alignment)
{
    void* p1; // original block
    void** p2; // aligned block
    int offset = alignment - 1 + sizeof(void*);
    if ((p1 = (void*)malloc(required_bytes + offset)) == NULL)
    {
       return NULL;
    }
    p2 = (void**)(((size_t)(p1) + offset) & ~(alignment - 1));
    p2[-1] = p1;
    return p2;
}

void aligned_free(void *p)
{
    free(((void**)p)[-1]);
}

void main (int argc, char *argv[])
{
    char **endptr;
    int *p = aligned_malloc (100, strtol(argv[1], endptr, 10));

    printf ("%s: %p\n", argv[1], p);
    aligned_free (p);
}

这个实现确实有效,但老实说,我不知道它是如何工作的。
我不明白的是:
1.为什么我们需要补偿?
1.使用~(alignment - 1)执行anding可以实现什么目的

  1. p2是一个双指针,为什么我们可以从一个只返回一个单指针的函数中返回它呢?
    1.解决这个问题的一般方法是什么?
    任何帮助都是非常感谢的。
    • 编辑**

这不是How to allocate aligned memory only using the standard library?的副本,因为我还需要知道如何释放对齐的内存。

1yjd4xko

1yjd4xko1#

1.如果要支持超出系统malloc()的对齐,则需要偏移量。例如,如果系统malloc()对齐到8字节边界,而您希望对齐到16字节,您要求额外的8个字节,这样您就可以确定您可以移动结果以按要求对齐它。您还可以将sizeof(void*)添加到传递给malloc()的大小中,以留出记帐空间。

  1. ~(alignment - 1)保证对齐。例如,如果对齐是16,那么减1得到15,也就是0xF,然后求反得到0xFF..FF0,这是你需要的掩码,以满足从malloc()返回的任何指针的对齐。注意,这个技巧假设对齐是2的幂(实际上它通常是,但真的应该有一个检查)。
    1.这是一个void**,函数返回void*,这是可以的,因为void的指针是“任何类型的指针”,在本例中,该类型是void*,换句话说,void*与其他指针类型之间的转换是允许的,双指针仍然是指针。
    1.这里的总体方案是将原始指针存储在返回给调用者的指针之前,标准malloc()的一些实现也做同样的事情:在返回的块之前隐藏簿记信息,这样就很容易知道在调用free()时需要回收多少空间。
    尽管如此,这类东西通常没有用,因为标准的malloc()返回系统上最大的对齐,如果您需要超出这个范围的对齐,可能有其他解决方案,包括编译器特定的属性。
myzjeezk

myzjeezk2#

实施确实有效
也许吧,但我不太确定。我想你最好从第一原则开始工作。马上,

p1 = (void*)malloc

是一个危险信号。malloc返回void。在C中,任何指针都可以从void *赋值。从malloc强制转换通常被认为是错误的形式,因为它所产生的任何影响都只能是错误的。
为什么我们需要补偿
偏移量提供空间来存放malloc返回的指针,该指针稍后将由free使用。
p1是从malloc中获取的。稍后,必须将其提供给free才能释放。aligned_mallocp1中保留sizeof(void*)字节,将p1隐藏在那里,并返回p2p1指向的块中的第一个“对齐”地址)。稍后,当调用程序将p2传递给aligned_free时,它实际上将p2转换为void *p2[],并使用-1作为索引来获取原始X1 M19 N1 X。
与~(alignment - 1)求与可实现什么
它把p2放在边界上,假设对齐是16;alignment -1为15,0xF。~OxF除最后4位外的所有位均为1。对于任何指针PP & ~0xF都将是16的倍数。
p2是一个双精度指针。
指针 schmointer. malloc返回void*。它是一个内存块;你想怎么说就怎么说你不会对

char **args = calloc(7, sizeof(char*));

分配一个7个char *指针的数组,您会这样做吗?代码从p1中选择了一些至少sizeof(void*)字节的“对齐”位置,并且出于free的目的,将其视为void **
一般的做法是什么
没有唯一的答案,最好的办法可能是使用一个标准如果你在malloc上构建,那么分配足够的空间来保留“真实的的”指针并返回一个对齐的指针是相当标准的,尽管我会用不同的方式编写它。syscall mmap返回一个页面对齐的指针,这将满足“对齐”的大多数条件。这可能比搭载在malloc上更好或更差。

iezvtpos

iezvtpos3#

假设我们需要SZ字节的对齐内存,令:

A is the alignment.
W is the CPU word size.
P is the memory returned by malloc.
SZ is the requested number of bytes to be allocated.

我们将返回**(P + Y)其中(P + Y)mod A = 0**
因此,我们应该保存原来的指针 P,以便以后能够释放内存。在这种情况下,我们应该分配**(SZ + W)字节,但为了让内存对齐,我们将子结构 Z字节 ,其中(P % A = Z)=〉(Z ∈ [0,A-1])

So the total memory to be allocated is:  SZ + W + MAX(Z) = SZ + W + A - 1

要返回的指针为P + Y = P + W + MAX(Z)-(P + W + MAX(Z))mod A
我们有:X - X模A =整数(X / A)A = X & ~(A - 1)
因此,我们可以用(P + W + MAX(Z))和~(A - 1)代替P + W + MAX(Z)-(P + W + MAX(Z))模A

The memory to be returned is: (P + W + MAX(Z)) & ~(A - 1) = (P + W + A - 1) & ~(A - 1)
gg58donl

gg58donl4#

我对这段代码有一些问题。我已经把它们编译成下面的列表:

  1. p1 = (void*)malloc不强制转换malloc的返回值。
  2. free(((void**)p)[-1]);您不能免费施法。
  3. if ((p1 = (void*)malloc(required_bytes + offset)) == NULL)不要把赋值语句放在if语句的比较语句中,我知道很多人都这样做,但在我看来,这只是一种糟糕的形式,会使代码更难阅读。
    他们在这里所做的是将原始指针存储在分配的块中。这意味着只有对齐的指针返回给用户。由malloc返回的实际指针,用户永远看不到。但是你必须保留那个指针,因为free需要它来将块从分配列表中断开链接,并将其放在空闲列表中。在每个内存块的头部,malloc在那里放了一些内务信息。比如下一个/上一个指针,大小,分配状态等等。一些调试版本的malloc使用保护字来检查是否有东西溢出缓冲区。传递给例程的对齐必须是2的幂。
    当我编写自己的malloc版本用于池化内存分配器时,我使用的最小块大小是8字节,因此包括32位系统的头,总共是28字节(20字节用于报头)。在64位系统上,它是40个字节(报头为32字节)。当数据与某个地址值对齐时,大多数系统的性能都会提高(在现代计算机系统中为4或8字节)。其原因是如果对齐,机器可以在一个总线周期内获取整个字。如果不对齐,则需要两个总线周期才能获取整个字。那么它必须构造它。2这就是为什么编译器在4或8字节上对齐变量。3这意味着地址总线的最后2或3位是零。
    我知道有一些硬件限制需要比默认的4或8更多的对齐。Nvidia的CUDA系统,如果我没记错的话,需要对齐到256字节...这是一个硬件要求。
    但以前也有人问过这个问题。请参见:How to allocate aligned memory only using the standard library?
    希望这个有用。

相关问题