今天突发奇想地想学习下内存管理(其实是报的腾讯终端开发,怕面试被问到如何实现内存管理模块)。于是找找资料,写了段代码,可以实现基于最佳适应法和循环首次适应法的内存分配。
大家都知道,我们malloc的时候操作系统维护着一张双链表记录堆里面的空闲内存块情况,每个节点对应一块内存。
最佳适应法:分配内存(大小为size)的时候,从表头开始搜索,找那块比size大的最小空闲内存块,进行分配,余下的部分变成一块空闲内存块插入到链表中
循环首次适应法:该算法是首次适应算法的变种。在分配内存空间时,不再每次从表头(链首)开始查找,而是从上次找到空闲区的下一个空闲开始查找,直到找到第一个能满足要求的的空闲区为止,并从中划出一块与请求大小相等的内存空间进行分配,余下的部分变成一块空闲内存块插入到链表中,并成为下一次分配搜索的起始内存块。
它们的代码如下(我们分配内存的单位是1KB):
//内存块首地址为56000(假定内存空间的低址部分56MB(即0~56M-1)作为系统区和不参与分配过程),总大小为200000
enum { ADDR = 56000, TOTAL = 200000, MAX = 20000, MIN = 100, CYCLE = 1, BEST = -1, FULL = 1, FREE = 0, N = 2000 };
struct node
{
struct node* prev; //前一个内存块
struct node* next; //后一个内存块
int number; //序列号
int addr; //首地址
int size; //大小
int status; //状态:空闲/使用
};
typedef struct node block;
void init ();
int request ();
int cycle ( int size );
int best ( int size );
/*occupied:占用的内存总量
count:占用的内存块数目
compare:比较次数
algo:算法
head:表头
tail:初始时的表尾
last:循环首次适应算法中下一次搜索的起始内存块
*/
int occupied = 0, count = 0, compare = 0, algo = CYCLE;
block *head = NULL, *tail = NULL, *last = NULL;
//初始化链表
void init ()
{
block *work, * temp;
occupied = 0;
count = 0;
compare = 0;
//如果链表不为空,清空链表
if ( head != NULL )
{
work = head->next;
while ( work != head )
{
temp = work;
work = work->next;
free(temp);
}
free(head);
}
//初始化链表头尾指针
head = (block*)malloc(sizeof(block));
tail = (block*)malloc(sizeof(block));
last = tail;
head->prev = tail;
head->next = tail;
tail->prev = head;
tail->next = head;
head->addr = 0;
head->size = ADDR;
head->status = FULL;
tail->addr = ADDR;
tail->size = TOTAL;
tail->status = FREE;
}
//请求分配内存
int request ()
{
int (*fp)(int);
int size = random(MIN,MAX), addr;
if ( algo == CYCLE )
{
fp = cycle;
}
else if ( algo == BEST )
{
fp = best;
}
printf("尝试申请一块大小为%dKB的内存",size);
if ( (addr = (*fp)(size)) == 0 )
{
printf(" 内存空间不足,分配失败\n");
return 0;
}
else
{
printf(" 在%d位置成功分配\n",addr);
return 1;
}
}
//循环首次适应算法
int cycle ( int size )
{
//从上一次空闲内存块(last指向)开始搜索
block* work = last, *start = last;
if ( last == NULL )
return 0;
//沿着链表方向搜索,找到一块大小满足要求的空闲内存块作为待分配内存块
while ( work->status == FULL || work->size < size )
{
work = work->next;
++compare;
//遍历完整个链表还没找到,返回0
if ( work == start )
return 0;
}
++compare;
//大小超过要分配的内存,当前内存块被分配,多出来的一部分变成空闲内存块插入到被分配的内存块之后,让last指向它
if ( work->size > size )
{
block* remain = (block*)malloc(sizeof(block));
remain->addr = work->addr + size;
remain->size = work->size - size;
remain->status = FREE;
last = remain;
work->size = size;
work->status = FULL;
remain->prev = work;
remain->next = work->next;
work->next->prev = remain;
work->next = remain;
}
else
{
//大小刚好,则last指向下一个空闲内存块
block* temp = work;
work->status = FULL;
while ( temp->status == FULL )
{
temp = temp->next;
if ( temp == work )
break;
}
if ( temp == work )
{
last = NULL;
}
else
{
last = temp;
}
}
++count;
occupied += size;
return work->addr;
}
//最佳适应算法
int best ( int size )
{
block* work = head->next, *fit;
//沿着链表头寻找未占用的,大小大于当前申请内存的内存块
while ( (work->status == FULL || work->size < size) && work != head )
{
work = work->next;
//每次判断,比较次数+1
++compare;
}
++compare;
//无法找到满足要求的内存块,返回0
if ( work == head )
return 0;
//当前内存块大小满足要求
fit = work;
//寻找大小满足要求的最小空闲内存块
while ( work != head )
{
work = work->next;
++compare;
if ( work->status == FREE && work->size >= size && work->size < fit->size )
fit = work;
}
//大小超过要分配的内存,当前内存块被分配,多出来的一部分变成空闲内存块插入到被分配的内存块之后
if ( fit->size > size )
{
block* remain = (block*)malloc(sizeof(block));
remain->addr = fit->addr + size;
remain->size = fit->size - size;
remain->status = FREE;
fit->size = size;
fit->status = FULL;
remain->prev = fit;
remain->next = fit->next;
fit->next->prev = remain;
fit->next = remain;
}
//大小正好,只改变当前内存块状态
else
{
fit->status = FULL;
}
//占用内存块数目+1
++count;
//占用内存块总大小增加
occupied += size;
//返回申请到的内存首地址
return fit->addr;
}