malloc 实现?
Posted
技术标签:
【中文标题】malloc 实现?【英文标题】:malloc implementation? 【发布时间】:2011-07-22 06:41:59 【问题描述】:我正在尝试为 C 实现 malloc
和 free
,但我不确定如何重用内存。我目前有一个struct
,看起来像这样:
typedef struct _mem_dictionary
void *addr;
size_t size;
int freed;
mem_dictionary;
我的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;
当我释放内存时,我会将地址标记为0
(这表明它是空闲的)。在我的malloc
中,我将使用for 循环在数组中查找任何等于0
的值,然后将内存分配给该地址。我有点困惑如何实现这一点。
【问题讨论】:
这里有一篇关于 dlmalloc 的精彩文章:g.oswego.edu/dl/html/malloc.html 【参考方案1】:最简单的方法是保留一个空闲块的链表。在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;
注意:(n + align_to - 1) & ~ (align_to - 1)
是将n
舍入为大于n
的最接近的align_to
倍数的技巧。这仅适用于 align_to
是 2 的幂并且取决于数字的二进制表示。
当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 - 1
添加到size
确保我们四舍五入到最接近的align_to
倍数(除非size
已经是align_to
的倍数,在这种情况下我们想要得到size
)。
【讨论】:
malloc函数第一行的神奇作用是什么? 它将(size + sizeof(size_t))
舍入为大于(size + sizeof(size_t))
的最接近的align_to
倍数。这仅适用于 align_to
是 2 的幂。
使用链表缓存来保持分配的内存(而不是重新分配)以加速图形程序(在 malloc 上花费太多时间)的类似技术被用作Jon Bentley 所著的 Programming Pearls 一书中第 9 栏:代码调优的第一部分中的示例。遗憾的是,这本书的示例中没有包含代码,所以看到这样的代码对我来说特别有用。【参考方案2】:
您不想将字典条目的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;
这不是一个很好的malloc()
实现。事实上,大多数malloc
/free
的实现都会为malloc 返回的每个块分配一个小的标头。例如,标头可能从比返回的指针少八 (8) 个字节的地址开始。在这些字节中,您可以存储指向拥有该块的mem_dictionary
条目的指针。这避免了free
中的 O(N) 操作。您可以通过实现释放块的优先级队列来避免malloc()
中的 O(N)。考虑使用以块大小为索引的二项式堆。
【讨论】:
对不起,我对 C 比较陌生,但是 malloc() 中的字典变量是什么? @no92 -- 我应该将其命名为“期刊”而不是“字典”。请记住,这是我的 example 和malloc
的简单实现。它至少有一个明显的缺陷:一次分配的块永远不能超过 1024 个。给出这个例子的唯一目的是给读者一个起点来实现他们自己的malloc
。这只是实现malloc
/free
库的概念基础。它甚至没有将realloc
作为另一个明显的缺陷来实现。它甚至可能不是最好的例子。 :)【参考方案3】:
我从 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;
【讨论】:
谢谢,我认为这个回复比 Sylvain 的回复稍微正确一些,因为我只是想知道这个。开销变量是一个非常好的主意,只是没有正确计算甚至没有使用。 谁能告诉我在malloc函数中head的使用? (free_block** head = &(free_block_list_head.next);
) 我没有看到它在任何地方使用。另外,为什么要在返回前加上sizeof(free_block)
?
head
是指向包含指针的地址的指针。它在 while 循环中用于取消链接返回给用户的内存块。添加和减去sizeof(free_block)
是一种常见而巧妙的技巧,可以向调用者“隐藏”元数据。
free() 实现中还有一个小错误,因为free(NULL)
会出现段错误。
您好,可以为这个实现添加合适的realloc
函数吗?以上是关于malloc 实现?的主要内容,如果未能解决你的问题,请参考以下文章