malloc实现?

use*_*257 27 c malloc free memory-management

我正在尝试实现mallocfreeC,我不知道如何重用内存.我目前struct看起来像这样:

typedef struct _mem_dictionary {
    void *addr;
    size_t size;
    int freed;
} mem_dictionary;
Run Code Online (Sandbox Code Playgroud)

malloc看起来像这样:

void *malloc(size_t size) {
    void *return_ptr = sbrk(size);
    if (dictionary == NULL) 
        dictionary = sbrk(1024 * sizeof(mem_dictionary));

    dictionary[dictionary_ct].addr = return_ptr;
    dictionary[dictionary_ct].size = size;
    dictionary[dictionary_ct].freed = 1;
    dictionary_ct++;

    return return_ptr;
}
Run Code Online (Sandbox Code Playgroud)

当我释放内存时,我只会将地址标记为0(表示它是免费的).在我看来malloc,我会使用for循环来查找数组中的任何值0,然后将内存分配给该地址.我有点困惑如何实现这一点.

Syl*_*sne 25

最简单的方法是保留一个空闲块的链表.在malloc,如果列表不为空,则搜索足够大的块以满足请求并返回它.如果列表为空或者找不到这样的块,则调用sbrk从操作系统分配一些内存.在free,您只需将内存块添加到空闲块列表中.作为奖励,您可以尝试合并连续释放的块,并且您可以更改选择要返回的块的策略(首先适合,最适合,...).如果块大于请求,您也可以选择拆分块.

一些示例实现(它未经过测试,显然不是线程安全的,使用风险自负):

typedef struct free_block {
    size_t size;
    struct free_block* next;
} free_block;

static free_block free_block_list_head = { 0, 0 };
static const size_t overhead = sizeof(size_t);
static const size_t align_to = 16;

void* malloc(size_t size) {
    size = (size + sizeof(size_t) + (align_to - 1)) & ~ (align_to - 1);
    free_block* block = free_block_list_head.next;
    free_block** head = &(free_block_list_head.next);
    while (block != 0) {
        if (block->size >= size) {
            *head = block->next;
            return ((char*)block) + sizeof(size_t);
        }
        head = &(block->next);
        block = block->next;
    }

    block = (free_block*)sbrk(size);
    block->size = size;

    return ((char*)block) + sizeof(size_t);
}

void free(void* ptr) {
    free_block* block = (free_block*)(((char*)ptr) - sizeof(size_t));
    block->next = free_block_list_head.next;
    free_block_list_head.next = block;
}
Run Code Online (Sandbox Code Playgroud)

注意:(n + align_to - 1) & ~ (align_to - 1)是一个圆形n到最接近的倍数的技巧align_to大于n.这仅在2 align_to的幂时有效并且取决于数字的二进制表示.

align_to为2的幂时,它只有一个位设置,因此align_to - 1具有所有最低位组(即align_to,形式为000 ... 010 ... 0,并且align_to - 1具有形式000...001...1).这意味着~ (align_to - 1)所有高位设置,低位未设置(即,它是形式111...110...0).因此x & ~ (align_to - 1)将所有低位设置为零x并将其向下舍入到最接近的倍数align_to.

最后,添加align_to - 1size确保我们向上舍入到最接近的倍数align_to(除非size已经是align_to我们想要获得的多个size).


Hea*_*utt 9

您不希望size将字典条目的字段设置为零 - 您将需要该信息以便重复使用.而是freed=1仅在块被释放时设置.

你不能合并相邻的块,因为可能有干预调用sbrk(),这样可以更容易.你只需要一个for循环来搜索一个足够大的释放块:

typedef struct _mem_dictionary
{
    void *addr;
    size_t size;
    int freed;
} mem_dictionary;


void *malloc(size_t size)
{
     void *return_ptr = NULL;
     int i;

     if (dictionary == NULL) {
         dictionary = sbrk(1024 * sizeof(mem_dictionary));
         memset(dictionary, 0, 1024 * sizeof(mem_dictionary));
     }

     for (i = 0; i < dictionary_ct; i++)
         if (dictionary[i].size >= size
          && dictionary[i].freed)
     {
         dictionary[i].freed = 0;
         return dictionary[i].addr;
     }

     return_ptr = sbrk(size);

     dictionary[dictionary_ct].addr = return_ptr;
     dictionary[dictionary_ct].size = size;
     dictionary[dictionary_ct].freed = 0;
     dictionary_ct++;

     return return_ptr;
}

void free(void *ptr)
{
    int i;

    if (!dictionary)
        return;

    for (i = 0; i < dictionary_ct; i++ )
    {
        if (dictionary[i].addr == ptr)
        {
            dictionary[i].freed = 1;
            return;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这不是一个很好的malloc()实现.实际上,大多数malloc/ free实现将为malloc返回的每个块分配一个小头.例如,标题可能从比返回指针八(8)个字节的地址开始.在这些字节中,您可以存储指向mem_dictionary拥有该块的条目的指针.这避免了O(N)操作free.您可以malloc()通过实现释放块的优先级队列来避免O(N).考虑使用二进制,块大小作为索引.


Kin*_*Jnr 5

我从Sylvain的回复中借用代码.他似乎错过了计算free_block*ini计算开销的大小.

总体而言,代码的工作原理是将此free_block作为标头添加到已分配的内存中.1.当用户调用malloc时,malloc会在此标头之后返回有效负载的地址.2.当调用free时,计算块头的起始地址(通过从块地址中减去头大小)并将其添加到空闲块池中.

typedef struct free_block {
    size_t size;
    struct free_block* next;
} free_block;

static free_block free_block_list_head = { 0, 0 };

// static const size_t overhead = sizeof(size_t);

static const size_t align_to = 16;

void* malloc(size_t size) {
    size = (size + sizeof(free_block) + (align_to - 1)) & ~ (align_to - 1);
    free_block* block = free_block_list_head.next;
    free_block** head = &(free_block_list_head.next);
    while (block != 0) {
        if (block->size >= size) {
            *head = block->next;
            return ((char*)block) + sizeof(free_block);
        }
        head = &(block->next);
        block = block->next;
    }

    block = (free_block*)sbrk(size);
    block->size = size;

    return ((char*)block) + sizeof(free_block);
}

void free(void* ptr) {
    free_block* block = (free_block*)(((char*)ptr) - sizeof(free_block ));
    block->next = free_block_list_head.next;
    free_block_list_head.next = block;
}
Run Code Online (Sandbox Code Playgroud)