操作系统第5次实验报告:内存管理
- 姓名:吴永锋
- 学号:201821121051
- 班级:计算1812
动态分区分配是根据进程的实际需要,动态的为之分配内存空间。而在实现可变分区分配时,将涉及到分区分配中
所用的数据结构、分区分配算法和分区的分配与内存回收的过程。
分区分配中的数据结构:(1)描述空闲块的数据结构。(2)内存块的描述。
1. 记录内存空间使用情况
分配内存区块表,包含被分配的内存区起始地址及大小和使用该内存的进程pid。
1 /*每个进程分配到的内存块描述*/ 2 typedef struct allocated_block{ 3 int pid; 4 int size; //进程大小 5 int start_addr; //进程分配到的内存块的起始地址 6 char process_name[NAME_LEN]; //进程名 7 struct allocated_block *next; //指向下一个进程控制块 8 }AB; 9 10 /*进程分配内存块链表的首指针*/ 11 AB *allocated_block_head = NULL;
2. 记录空闲分区
空闲内存区块表,包含该空闲区的起始地址以及大小。程序初始化链表仅一个节点,大小为默认大小。
1 /*描述每一个空闲块的数据结构*/ 2 typedef struct free_block_type{ 3 int size; //空闲块大小 4 int start_addr; //空闲块起始位置 5 struct free_block_type *next; //指向下一个空闲块 6 }FBT; 7 8 FBT *free_block;//指向内存中空闲块链表的首指针
3. 内存分配算法
首次适应算法(First Fit):从空闲分区表的第一个表目起查找该表,把最先能够满足要求的空闲区分配给
作业,这种方法的目的在于减少查找时间。为适应这种算法,空闲分区表(空闲区链)中的空闲分区要按地址由低到
高进行排序。该算法优先使用低址部分空闲区,在低址空间造成许多小的空闲区,在高地址空间保留大的空闲区。
1 //执行分配内存 2 void do_allocate_mem(AB *ab){ 3 int request = ab->size; 4 FBT *tmp = free_block; 5 while(tmp != NULL){ 6 if(tmp->size >= request){ 7 //分配 8 ab->start_addr = tmp->start_addr; 9 int shengyu = tmp->size - request; 10 tmp->size = shengyu; 11 tmp->start_addr = tmp->start_addr + request; 12 13 return ; 14 } 15 tmp = tmp->next; 16 } 17 } 18 19 int allocate_mem(AB *ab){ 20 /*分配内存模块*/ 21 FBT *fbt,*pre; 22 int request_size=ab->size; 23 fbt = pre = free_block; 24 /* 25 根据当前算法在空闲分区链表中搜索合适空闲分区进行分配, 26 分配时注意以下情况: 27 1. 找到可满足空闲分区且分配后剩余空间足够大,则分割 28 2. 找到可满足空闲分区且但分配后剩余空间比较小,则一起分配 29 3. 找不可满足需要的空闲分区但空闲分区之和能满足需要, 30 则采用内存紧缩技术,进行空闲分区的合并,然后再分配 31 4. 在成功分配内存后,应保持空闲分区按照相应算法有序 32 5. 分配成功则返回1,否则返回-1 33 */ 34 //尝试寻找可分配空闲,具体结果在函数中有解释 35 int f = find_free_mem(request_size); 36 if(f == -1){ 37 //不够分配 38 printf("空闲内存不足,内存分配失败!\n"); 39 return -1; 40 }else{ 41 if(f == 0){ 42 //需要内存紧缩才能分配 43 memory_compact(); 44 } 45 //执行分配 46 do_allocate_mem(ab); 47 } 48 //重新排布空闲分区 49 rearrange(ma_algorithm); 50 return 1; 51 } 52 //为进程分配内存 53 int alloc_process(Prc prc){ 54 AB *ab; 55 int ret; 56 ab = (AB*)malloc(sizeof(AB)); 57 if(!ab) exit(-5); 58 /*为ab赋值 */ 59 ab->next=NULL; 60 pid++;//记录id 61 strcpy(ab->process_name,prc.process_name); 62 ab->pid = pid; 63 ab->size=prc.size+rand()%ALLOC_SIZE;//随机分配内存 64 65 ret = allocate_mem(ab); //从空闲分区分配内存,ret==1表示分配成功 66 if((ret == 1) && (allocated_block_head == NULL)){ 67 /*如果此时allocated_block_head尚未赋值,则赋值*/ 68 allocated_block_head = ab; 69 return 1; 70 }else if(ret == 1){ 71 /*分配成功,将该分配块的描述插入已分配链表*/ 72 ab->next = allocated_block_head; 73 allocated_block_head = ab; 74 return 2; 75 }else if(ret == -1){ 76 //分配不成功 77 printf("\e[0;31;1m 内存分配失败! \e[0m\n"); 78 free(ab); 79 return -1; 80 } 81 return 3; 82 } 83 84 void rearrange_FF(){ 85 /*首次适应算法,空闲区大小按起始地址升序排序*/ 86 //这里使用冒泡排序方法 87 if(free_block == NULL || free_block->next == NULL) 88 return; 89 FBT *t1,*t2,*head; 90 head = free_block; 91 for(t1 = head->next;t1;t1 = t1->next){ 92 for(t2 = head;t2 != t1;t2=t2->next){ 93 if(t2->start_addr > t2->next->start_addr){ 94 int tmp = t2->start_addr; 95 t2->start_addr = t2->next->start_addr; 96 t2->next->start_addr = tmp; 97 98 tmp = t2->size; 99 t2->size = t2->next->size; 100 t2->next->size = tmp; 101 } 102 } 103 } 104 }
4. 内存释放算法
找到对应的链表节点,释放释放杀死进程的内存块,归还进程的已分配的存储空间,按大小从小到大排序,销毁杀死进程的结点。
1 //释放链表节点 2 int dispose(AB *free_ab){ 3 /*释放ab数据结构节点*/ 4 AB *pre,*ab; 5 if(free_ab == allocated_block_head){ 6 //如果要是释放第一个节点 7 allocated_block_head = allocated_block_head->next; 8 free(free_ab); 9 return 1; 10 } 11 pre = allocated_block_head; 12 ab = allocated_block_head->next; 13 while(ab!=free_ab){ 14 pre = ab; 15 ab = ab->next; 16 } 17 pre->next = ab->next; 18 free(ab); 19 return 2; 20 } 21 22 //更新分区表 23 int free_mem(AB *ab){ 24 /* 将ab所表示的已分配区归还,并进行可能的合并 */ 25 int algorithm = ma_algorithm; 26 FBT *fbt,*pre,*work; 27 fbt = (FBT*)malloc(sizeof(FBT)); 28 if(!fbt) return -1; 29 /* 30 进行可能的合并,基本策略如下? 31 1. 将新释放的结点插入到空闲分区队列末尾? 32 2. 对空闲链表按照地址有序排列? 33 3. 检查并合并相邻的空闲分区? 34 4. 将空闲链表重新按照当前算法排序 35 */ 36 fbt->size = ab->size; 37 fbt->start_addr = ab->start_addr; 38 39 //插至末尾 40 work = free_block; 41 if(work == NULL){ 42 free_block = fbt; 43 fbt->next == NULL; 44 }else{ 45 while(work ->next != NULL){ 46 work = work->next; 47 } 48 fbt->next = work->next; 49 work->next = fbt; 50 } 51 //按地址重新排布 52 rearrange_FF(); 53 54 //合并可能分区;即若两空闲分区相连则合并 55 pre = free_block; 56 while(pre->next){ 57 work = pre->next; 58 if(pre->start_addr + pre->size == work->start_addr ){ 59 pre->size = pre->size + work->size; 60 pre->next = work->next; 61 free(work); 62 continue; 63 }else{ 64 pre = pre->next; 65 } 66 } 67 68 //按照当前算法排序 69 rearrange(ma_algorithm); 70 return 1; 71 } 72 73 int kill_process(int pid){ 74 AB *ab; 75 ab = find_process(pid); 76 if(ab!=NULL){ 77 free_mem(ab); //释放ab所表示的分配表 78 dispose(ab); //释放ab数据结构节点 79 return 0; 80 }else{ 81 return -1; 82 } 83 }
5. 运行结果
(1)产生测试数据
随机为3个进程分配、释放内存10次以上,即随机产生10组以上数据:(进程Pi 分配内存大小) 或者 (进程Pi结束)
int main(int argc, char const *argv[]){ /* code */ int sel1,sel2; int total=0; //记录分配内存的次数 free_block = init_free_block(mem_size); //初始化空闲区 Prc prc[PROCESS_NUM];//存放要加载的进程 init_program(prc,PROCESS_NUM);//对这几个程进程进行初始化 srand( (unsigned)time( NULL ) ); for(int i=0;i<DATA_NUM;++i) { /* sel1=0表示为某进程分配内存空间 sel1=1表示为释放某进程占用的内存空间 */ sel1=rand()%2; int count=0; //统计三个进程中有多少个进程已经分配内存 for(int j=0;j<PROCESS_NUM;++j){ if(prc[j].pid!=-1) count++; } //如果全部分配进程或者进程分配到达5次,那么就不能继续分配内存改为释放内存 if((count==PROCESS_NUM && sel1==0)||total==5) sel1=1; //如果全部未分配进程,那么就不能继续释放内存 if(count==0 && sel1==1) sel1=0; if(sel1==0)//为进程分配内存 { //随机找到一个未分配内存的进程 do{ sel2=rand()%PROCESS_NUM; }while(prc[sel2].pid!=-1); alloc_process(prc[sel2]);//分配内存空间 prc[sel2].pid=pid;//改变标记 total++; display_mem_usage();//显示 } else//释放进程占用的内存空间 { //随机找到一个可释放进程 do{ sel2=rand()%PROCESS_NUM; }while(prc[sel2].pid==-1); kill_process(prc[sel2].pid);//释放内存空间 prc[sel2].pid=-1;//改变标记 display_mem_usage();//显示 } } }
(2)解释结果
程序中定义了内存大小为1024,
第一次,为进程process-03分配内存,起始地址为0,大小为106,分配后剩余空闲分区内存起始地址为106,大小为918。
第二次,为进程process-01分配内存,起始地址为106,大小为57,分配后剩余空闲分区内存起始地址为163,大小为861。
第三次,释放进程process-01的内存空间,释放后剩余空闲分区内存起始地址为106,大小为918。
第四次,释放进程process-03的内存空间,释放后剩余空闲分区内存起始地址为0,大小为1024。
参考博客:内存分配---FF、BF、WF三种算法