整体的设计:使用分离存储的方案,维护一个存储空闲链表的数组free_block_list,每个大小类一个空闲链表,根据2的幂来划分大小,块的设计采用书本上介绍的显示空闲链表的方案。
当可供使用的空间不够时,就会扩展堆。在extend_heap中会增加堆顶的大小,并将新的空闲块挂载到free_block_list上。为了减少内存碎片,会根据新申请的块的前一个块是否空闲来决定是否与其合并。
void *extend_heap(size_t size)
{
void *ptr;
// align
size = ALIGN(size);
// 增加堆的大小
if ((ptr = mem_sbrk(size)) == (void *)-1)
return NULL;
// 初始化新申请的空闲块
PUT(HDRP(ptr), PACK(size, 0));
PUT(FTRP(ptr), PACK(size, 0));
/* 注意这个块是堆的结尾,所以还要设置一下结尾 */
PUT(HDRP(NEXT_BLKP(ptr)), PACK(0, 1));
// 将新申请的空闲块挂到free_block_list上
insert_node(ptr, size);
// 合并空闲块
return merge_free_block(ptr);
}
insert_node实现了将一个空闲块插入到free_block_list的操作,free_block_list中一共有16条链表,下标由0到15升序排列,free_block_list中的元素指向队列的队尾
。将ptr插入block_free_list,链表从大到小排序,在insert_node中逆序遍历链表查找到对应的位置,将空闲块插入。需要注意的是,当链表为空时free_block_list中对应的元素为NULL。
void insert_node(void *ptr, size_t size)
{
int idx = 0;
void *next_node = NULL;
void *pre_node = NULL;
// 找到相应的桶
for (;(idx < FREE_LIST_LENGTH - 1) && (size > 1); size >>= 1)
idx++;
next_node = free_block_list[idx];
// next_node为pre_node的前一个结点
for (; (next_node != NULL) && (size > GET_SIZE(HDRP(next_node))); next_node = GET_PRED_NODE(next_node))
pre_node = next_node;
if (next_node != NULL && pre_node != NULL) { // case 1
ASSIGN(GET_PRED_ADDR(ptr), next_node);
ASSIGN(GET_NEXT_ADDR(next_node), ptr);
ASSIGN(GET_NEXT_ADDR(ptr), pre_node);
ASSIGN(GET_PRED_ADDR(pre_node), ptr);
}
else if (next_node != NULL && pre_node ==