- 姓名:倪晓东
- 学号: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