C中动态内存分配器的自定义实现
Posted
技术标签:
【中文标题】C中动态内存分配器的自定义实现【英文标题】:Custom implementation of dynamic memory allocators in C 【发布时间】:2021-07-24 22:13:50 【问题描述】:所以对于我的 C 任务,我需要实现一个动态内存分配器,它具有与标准库(如 malloc、free、realloc)类似的接口。我将分配器实现为可以由其他程序调用的函数库。虚拟堆将由一个简单的伙伴分配算法管理。
我给出的功能是:
void * virtual_sbrk(int32_t increment);
pretty much the same as the real-world sbrk and brk syscalls. I don't need to implement this.
void init_allocator(void * heapstart, uint8_t initial_size, uint8_t min_size);
This function will be called once at the beginning and initialise the virtual heap.
void * virtual_malloc(void * heapstart, uint32_t size);
mallocs memory
int virtual_free(void * heapstart, void * ptr);
frees memory
void * virtual_realloc(void * heapstart, void * ptr, uint32_t size);
reallocates memory
void virtual_info(void * heapstart);
prints the current state of the buddy allocator to standard output.
这是我目前的问题: 你如何初始化堆并在没有任何东西的情况下实现 malloc?就像我不能使用 malloc 或任何预先存在的分配器函数一样。到目前为止,我已经尝试使用带有包含内存作为值的节点的链表。例如,如果初始大小为 3,最小大小为 1,我将有 5 个节点,根节点包含 8 个字节,另外两个节点每个包含 4 个字节,最后还有 2 个节点,每个节点包含 2 个字节。但是我仍然对如何使用 sbrk 或堆的结构感到困惑。我浏览了在线资源,但仍然对如何构建堆内存感到困惑。
以下是我目前的代码:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
struct node
size_t memory;
struct node *nextInLine;
;
void printNode(const struct node *nd, const char *comment)
if(nd == NULL)
printf("%s is null\n", comment);
else
printf("%s: memory:%d address:%p nextInLine:%p\n",
comment,
nd->memory,
nd,
nd->nextInLine);
void printList(const struct node *list)
printf("Printing List:\n");
const struct node *t;
t = list;
if(t == NULL)
printf("current node is empty\n");
else
while(t)
printNode(t, "node");
t = t->nextInLine;
void * virtual_sbrk(int32_t increment)
void *p = malloc(increment);
return p;
uint8_t return_init_size(uint8_t size)
return size;
struct node *getNewNode(const uint8_t memory_size)
struct node *newNode = NULL;
double two = 2;
size_t m_size = memory_size;
double result = pow(two, m_size);
newNode = virtual_sbrk(result);
if(newNode != NULL)
newNode->memory = result;
newNode->nextInLine = NULL;
else
printf("Allocation error: newNode is still NULL\n");
return newNode;
void init_allocator(void * heapstart, uint8_t initial_size, uint8_t min_size)
//error catchers
if(initial_size == 0)
printf("Initial size is 0\n");
if(initial_size < min_size)
printf("initial_size is smaller than min_size\n");
//initialising the virtual heap using a linked array with nodes the memory size of 2^some_size
uint8_t i = initial_size;
struct node *first = heapstart;
heapstart = first;
struct node *tail = NULL;
while(i >= min_size)
if(first == NULL)
first = getNewNode(i);
if(first != NULL)
tail = first;
else
tail->nextInLine = getNewNode(i);
if(tail->nextInLine != NULL)
tail = tail->nextInLine;
tail->nextInLine = getNewNode(i);
if(tail->nextInLine != NULL)
tail = tail->nextInLine;
i -= 1;
printList(first);
void * virtual_malloc(void * heapstart, uint32_t size)
if(size == 0)
return NULL;
return NULL;
int virtual_free(void * heapstart, void * ptr)
return 1;
void * virtual_realloc(void * heapstart, void * ptr, uint32_t size)
return NULL;
void virtual_info(void * heapstart)
如果有人可以帮助解释我将如何执行此操作,那就太好了,就像我需要遵循的结构一样,如果这有意义的话。
【问题讨论】:
您可以使用免费列表。这实际上是 malloc 的工作原理,请在此处查看更多信息 how-do-free-and-malloc-work 你可以使用一个大的全局数组,即'char pool[1000000];' 【参考方案1】:您可以像 glibc malloc 一样使用 sbrk
和 mmap
。
glibc malloc 与线程一起工作,使用称为 arenas 的东西。
malloc 初始化时会调用sbrk
来扩展映射内存。
当发生大分配或创建新线程时,malloc 最终会调用 mmap
。
mmap
在进程的地址空间中分配一个新的映射。
sbrk
扩展当前映射以使其更大。
sbrk
的简单示例:
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#define HEAP_SZ 0x8000
int main(void)
void *p = sbrk(0);
printf("current break addr = %p\n", p);
sbrk(HEAP_SZ);
void *n = sbrk(0);
printf("new break addr = %p\n", n);
return 0;
第一次调用(带参数 0)返回当前程序中断。 当指定大于 0 的大小时,程序中断被扩展,因此在下次使用参数 0 调用时,将返回新的程序中断。
你可以这样做:
unsigned long heap_mem_sz = 0;
void *heap_start_addr = NULL;
void init_heap(void)
void *p = sbrk(0);
#if DEBUG
printf("current break addr = %p\n", p);
#endif
sbrk(HEAP_SZ);
heap_mem_sz = (unsigned long)HEAP_SZ;
void *n = sbrk(0);
#if DEBUG
printf("new break addr = %p\n", n);
#endif
heap_start_addr = (void *)n;
return;
拥有关于全局变量的信息可以让您继续开发分配器实现。
您可以在第一次请求分配时致电init_heap()
。
现在您可以返回该分配并制作“顶部块”。
它将是一个结构与其他块相同的块,但包含分配从中获取内存的所有内存,并且在分配时它会缩小。
此外,一旦堆内存已满,您将需要执行一些操作,因此请考虑再次调用 mmap
或 sbrk
之类的系统调用。
malloc 上的链表用于 bin。它们用于搜索可以满足新分配的已释放块,以便您重用不再使用的块。
对于这样的链表,你可以创建一个全局的:
struct heap_chunk *freed_chain = NULL
请求内存时,首先检查freed_chain
是否为NULL,如果不是,则遍历链表,直到找到与用户请求兼容的块,或者下一个指针为NULL。
如果这些块中的任何一个是有效的,您将需要从链表中取消该块的链接,并使前一个块指向下一个块,因此不再有内存请求访问它,因为它现在已分配且未释放.
在释放内存时,您需要将一个新块链接到该链表。
显然在 malloc 上,出于优化目的,这更复杂,并且存在一些具有不同大小要求和不同属性的不同 bin 以加快分配速度。
【讨论】:
以上是关于C中动态内存分配器的自定义实现的主要内容,如果未能解决你的问题,请参考以下文章