- 姓名:倪晓东
- 学号:201821121020
- 班级: 计算1811
1. 记录内存空间使用情况
解释你是如何记录内存空间使用情况,给出关键代码。
/*记录内存空间使用情况,每个进程分配到的内存块描述*/
struct allocated_block
{
int pid;
int size; //进程大小
int start_addr; //进程分配到的内存块的起始地址
char process_name[PROCESS_NAME_LEN]; //进程名
struct allocated_block *next; //指向下一个进程控制块
};
2. 记录空闲分区
用什么样的方法记录内存空闲区,给出关键代码。
//空闲分区,描述每一个空闲块的数据结构
struct free_block_type
{
int size; //空闲块大小
int start_addr; //空闲块起始位置
struct free_block_type *next; //指向下一个空闲块
};
//指向内存中空闲块链表的首地址 struct free_block_type *free_block= NULL;
空闲分区链表:
//按首次适配算法重新整理内存空闲块链表,按空闲块首地址排序
int rearrange_FF()
{
struct free_block_type *head= free_block;
struct free_block_type *forehand,*pre,*rear;
int i;
if(head== NULL)
return -1;
for(i= 0;i< free_block_count-1;i++)
{
forehand= head;
pre= forehand->next;
rear= pre->next;
while(pre->next!= NULL)
{
if(forehand== head&&forehand->start_addr>= pre->start_addr)
{
//比较空闲链表中第一个空闲块与第二个空闲块的开始地址的大小
head->next= pre->next;
pre->next= head;
head= pre;
forehand= head->next;
pre= forehand->next;
rear= pre->next;
}
else if(pre->start_addr>= rear->start_addr)
{
//比较链表中其它相邻两个结点的开始地址的大小
pre->next= rear->next;
forehand->next= rear;
rear->next= pre;
forehand= rear;
rear= pre->next;
}
else
{
forehand= pre;
pre= rear;
rear= rear->next;
}
}
}
return 0;
}
3. 内存分配算法
使用首次适配算法内存分配:
//按照首次适应算法给新进程分配内存空间
int allocate_FF(struct allocated_block *ab)
{
int ret;
struct free_block_type *pre= NULL,*ff= free_block;
if(ff== NULL)
return -1;
while(ff!= NULL)
{
if(ff->size>= ab->size)
{
ret= allocate(pre,ff,ab);
break;
}
pre= ff;
pre= pre->next;
}
if(ff== NULL&¤t_free_mem_size> ab->size)
ret= mem_retrench(ab);
else
ret= -2;
rearrange_FF();
return ret;
}
4. 内存释放算法
int exit()
{
struct allocated_block *allocated_ab,*allocated_pre;
struct free_block_type *free_ab,*free_pre;
free_pre= free_block;
allocated_pre= allocated_block_head;
if(free_pre!= NULL) //链表不为空
{
free_ab= free_pre->next;
while(free_ab!= NULL)
{
free(free_pre); //释放当前节点
free_pre= free_ab;
free_ab= free_ab->next; //节点后移
}
}
if(allocated_pre!= NULL)
{
allocated_ab= allocated_pre->next;
while(allocated_ab!= NULL)
{
free(allocated_pre); //释放节点
allocated_pre= allocated_ab;
allocated_ab= allocated_ab->next; //节点后移
}
}
allocated_ab= allocated_ab->next;
return 0;
}
删除进程,归还分配的存储空间,并删除描述该进程内存分配的结点
void kill_process()
{
struct allocated_block *ab;
int pid;
printf("Kill Process,pid=");//删除进程号为
scanf("%d",&pid);
getchar();
ab= find_process(pid); //找到pid对应的链表节点
if(ab!= NULL)
{
free_mem(ab); //释放ab所表示的分配区
dispose(ab); //释放ab数据结构结点
}
}
在进程分配链表中寻找指定进程:
struct allocated_block* find_process(int pid)
{
struct allocated_block *ab= allocated_block_head;
if(ab== NULL)
{
printf("不存在!\n");
return NULL;
}
while(ab->pid!= pid&&ab->next!= NULL)//查找整个链表
ab= ab->next;
if(ab->next== NULL&&ab->pid!= pid)//找不到
{
printf("error!\n");
return NULL;
}
return ab;
}
5. 运行结果
(1)产生测试数据
设置内存大小为1000,每次为进程分配1-100内存大小:

设置内存大小为200,每次为进程分配1-20内存大小:

(2)解释结果

内存总大小为200,每次为进程分配1-20内存
如图所示,创建的第一个进程号PID=1,进程名称为PEOCESSNAME-01,内存起始地址为0,内存大小为2,空闲区首地址为2,剩余内存198。

如图所示,创建的第二个进程号PID=2,进程名称为PEOCESS-02,内存起始地址为2,分配到的内存大小为8,空闲区首地址为10,剩余内存190。

如图所示,创建的第二个进程号PID=3,进程名称为PEOCESS-03,内存起始地址为10,分配到的内存大小为15,空闲区首地址为25,剩余内存175。

如图所示,创建的第二个进程号PID=4,进程名称为PEOCESS-04,内存起始地址为25,分配到的内存大小为1,空闲区首地址为26,剩余内存174。
小结:
首次适配算法从空闲区的第一个表目起查找该表,把最先能够满足要求的空闲区分配给进程,这种方法目的在于减少查找时间。为适应这种算法,空闲区链中的空闲分区要按地址由低到高进行排序。该算法优先使用低址部分空闲区,在低址空间造成许多小的空闲区,在高地址空间保留大的空闲区。但是在实际使用过程中,由于优先使用低址的可用空闲区,低址部分被不断进行分配,但又不能保证刚好完全利用,所以会留下很多不能被使用小空闲区,而且每次为进程分配内存从低址开始寻找,就要和这些小空闲区适配,会影响速率。
参考文献:https://github.com/City-Zero/LinuxTest/blob/master/OSex/mem_manager/mm.c
