今天来续写,觉得昨天那种安排不合理,于是将原理与应用分两篇来写,不至于让大家和我看的有些烦。
温故而知新可以为师矣,如果不能为师,给自己当老师也不错哦。好了,写一个模拟内存管理的程序吧,老师说有原理,也要有具体实现,这是提升分析问题的一种途径。下面写写程序,并进行解说,这个程序我当时也完善了一下。
先写一些宏定义和全局变量
1 #define PROCESS_NAME_LEN 32 //进程名称的最大长度 2 #define MIN_SLICE 10 //最小碎片的大小 3 #define DEFAULT_MEM_SIZE 1024 //默认内存的大小 4 #define DEFAULT_MEM_START 0 //默认内存的起始位置 5 6 // 内存分配算法 7 #define MA_FF 1 //首次适应算法,地址递增 8 #define MA_BF 2 //最佳适应算法,空闲区容量从大到小 9 #define MA_WF 3 //循环首次适应算法, 10 11 int mem_size=DEFAULT_MEM_SIZE; //内存大小 12 int ma_algorithm = MA_FF; //当前分配算法 13 static int pid = 0; //初始pid 14 int flag = 0; //设置内存大小标志
哦,以前写的时候就有注释,所以碰到一些注释不明确的我在解释解释。
下面是几个重要的数据结构,看过linux内核代码的童鞋对操作系统的数据结构应该有所了解,其实也就是比我们平时用的更深入、更精巧而已,基本原理还是一样,有些只是我们没那么用过,我这个还是一般的用法。
1 //描述每一个空闲块的数据结构 2 struct free_block_type{ 3 int size; 4 int start_addr; 5 struct free_block_type *next; 6 }; 7 8 //指向内存中空闲块链表的首指针 9 struct free_block_type *free_block; 10 11 /*每个进程分配到的内存块的描述*/ 12 struct allocated_block{ 13 int pid; 14 int size; 15 int start_addr; 16 char process_name[PROCESS_NAME_LEN]; 17 struct allocated_block *next; 18 }; 19 20 //进程分配内存块链表的首指针 21 struct allocated_block *allocated_block_head = NULL;
linux内核也是这样命名的,基本都能见名知意,我就不费话了。
1 /*初始化空闲块,默认为一块,可以指定大小及起始地址*/ 2 struct free_block_type* init_free_block(int mem_size){ 3 struct free_block_type *fb; 4 5 fb=(struct free_block_type *)malloc(sizeof(struct free_block_type)); 6 if(fb==NULL){ 7 printf("No mem\n"); 8 return NULL; 9 } 10 fb->size = mem_size; 11 fb->start_addr = DEFAULT_MEM_START; 12 fb->next = NULL; 13 printf("init_free_block fished\n\n"); 14 return fb; 15 }
写过单片机或者ARM程序的同学可能对初始化有比较深刻的理解,因为在做一些事情之前,总是要做一些准备工作,举个最简答的例子,吃饭之前要做什么准备工作,洗手?错了,我说的不是这个。吃饭你得有筷子、碗和饭菜,这些就是初始化工作,接下来你才能吃饭呀。
1 //显示菜单 2 void display_menu(){ 3 printf("\n"); 4 printf("1 - Set memory size (default=%d)\n", DEFAULT_MEM_SIZE); 5 printf("2 - Select memory allocation algorithm\n"); 6 printf("3 - New process \n"); 7 printf("4 - Terminate a process \n"); 8 printf("5 - Display memory usage \n"); 9 printf("0 - Exit\n"); 10 }
这个相当于菜单,就这样。
1 //设置内存的大小 2 int set_mem_size(){ 3 int size; 4 if(flag!=0){ //防止重复设置 5 printf("Cannot set memory size again\n"); 6 return 0; 7 } 8 printf("Total memory size ="); 9 scanf("%d", &size); 10 if(size>0) { 11 mem_size = size; 12 free_block->size = mem_size; 13 } 14 flag=1; 15 return 1; 16 }
这个其实也相当于初始化,只不过你有了选择权。
1 // 设置当前的分配算法 2 void set_algorithm(){ 3 int algorithm; 4 printf("\t1 - First Fit\n"); //首次适应算法,简称FF 5 printf("\t2 - Best Fit \n"); //最佳适应算法 6 printf("\t3 - Worst Fit \n"); //最差适应算法 7 scanf("%d", &algorithm); 8 if(algorithm>=1 && algorithm <=3) 9 ma_algorithm=algorithm; 10 11 rearrange(ma_algorithm); //按指定算法重新排列空闲区链表 12 } 13 14 //按指定的算法整理内存空闲块链表 15 void rearrange(int algorithm){ 16 switch(algorithm){ 17 case MA_FF: rearrange_FF(); break; 18 case MA_BF: rearrange_BF(); break; 19 case MA_WF: rearrange_WF(); break; 20 } 21 } 22 //按FF算法重新整理内存空闲块链表 23 void rearrange_FF(){ 24 25 struct free_block_type *position, *min, *current; 26 int size; 27 int start_addr; 28 printf("Rearrange free blocks for FF \n\n"); 29 position = free_block; 30 31 //空闲块按地址顺序进行简单选择排序 32 while (position != NULL) { 33 min = position; 34 current = position->next; 35 36 while(current != NULL){ 37 if( current->start_addr < min->start_addr) { 38 min = current; 39 } 40 current = current->next; 41 } 42 43 size = position->size; 44 position->size = min->size; 45 min->size = size; 46 47 start_addr = position->start_addr; 48 position->start_addr = min->start_addr; 49 min->start_addr = start_addr; 50 51 position = position->next; 52 } 53 } 54 55 //按BF算法重新整理内存空闲块链表 56 void rearrange_BF(){ 57 58 struct free_block_type *position, *min, *current; 59 int size; 60 int start_addr; 61 printf("Rearrange free blocks for BF\n\n"); 62 position = free_block; 63 64 //空闲块按由小到大进行简单选择排序 65 while (position != NULL) { min = position; 66 current = position->next; 67 68 while(current != NULL){ 69 if( current->size < min->size) { 70 min = current; 71 } 72 current = current->next; 73 } 74 75 size = position->size; 76 position->size = min->size; 77 min->size = size; 78 79 start_addr = position->start_addr; 80 position->start_addr = min->start_addr; 81 min->start_addr = start_addr; 82 83 position = position->next; 84 } 85 } 86 87 88 //按WF算法重新整理内存空闲块链表 89 void rearrange_WF(){ 90 struct free_block_type *position, *min, *current; 91 int size; 92 int start_addr; 93 printf("Rearrange free blocks for WF \n\n"); 94 position = free_block; 95 96 //空闲块按由大到小进行简单选择排序 97 while (position != NULL) { min = position; 98 current = position->next; 99 100 while(current != NULL){ 101 if( current->size > min->size) { 102 min = current; 103 } 104 current = current->next; 105 } 106 107 size = position->size; 108 position->size = min->size; 109 min->size = size; 110 111 start_addr = position->start_addr; 112 position->start_addr = min->start_addr; 113 min->start_addr = start_addr; 114 115 position = position->next; 116 } 117 }
这段是按三种排序算法进行内存整理,如果你了解这三种排序算法,就没必要看这些了,我觉得只要对算法核心思想理解了,写基本是没问题的,最大的差异可能就是效率和风格。
1 //创建新的进程,主要是获取内存的申请数量 2 int new_process(){ 3 struct allocated_block *ab; 4 int size; 5 int ret; 6 ab=(struct allocated_block *)malloc(sizeof(struct allocated_block)); 7 if(!ab) exit(-5); 8 ab->next = NULL; 9 pid++; 10 sprintf(ab->process_name, "PROCESS-%02d", pid); //格式化字符串复制,将引号中数据复制到ab->process_name字符数组中去 11 ab->pid = pid; 12 13 printf("Memory for %s:", ab->process_name); 14 scanf("%d", &size); 15 if(size>0) ab->size=size; 16 ret = allocate_mem(ab); // 从空闲区分配内存,ret==1表示分配ok 17 //如果此时allocated_block_head尚未赋值,则赋值 18 if((ret==1) &&(allocated_block_head == NULL)){ 19 allocated_block_head=ab; 20 printf("new_process was created\n\n"); 21 return 1; 22 } 23 //分配成功,将该已分配块的描述插入已分配链表 24 else if (ret==1) { 25 ab->next=allocated_block_head; 26 allocated_block_head=ab; 27 printf("new_process was created\n\n"); 28 return 2; 29 } 30 else if(ret==-1){ //分配不成功 31 printf("Allocation fail\n"); 32 free(ab); 33 return -1; 34 } 35 return 3; 36 }
这部分主要是创建线程并作一些初始化工作。
1 //分配内存模块 2 int allocate_mem(struct allocated_block *ab){ 3 struct free_block_type *fbt, *pre; 4 int request_size=ab->size; 5 int sum_size = 0; 6 fbt = pre = free_block; 7 8 while(fbt!=NULL){ 9 10 if( (fbt->size - request_size) >= MIN_SLICE ){ //分配后空闲空间足够大,则分割 11 ab->start_addr = fbt->start_addr; 12 fbt->size = fbt->size - request_size; 13 fbt->start_addr = fbt->start_addr + request_size; 14 rearrange(ma_algorithm); 15 return 1; 16 } 17 else if( (fbt->size > request_size) && (fbt->size - request_size < MIN_SLICE) ){ //分割后空闲区成为小碎片,一起分配 18 ab->start_addr = fbt->start_addr; 19 ab->size = fbt->size; 20 if(fbt == free_block) { 21 free_block = fbt->next; 22 free(fbt); 23 } 24 else{ 25 pre->next = fbt->next; 26 free(fbt); 27 } 28 rearrange(ma_algorithm); 29 return 1; 30 } 31 32 sum_size += fbt->size; 33 pre = fbt; 34 fbt = fbt->next; 35 } 36 if(sum_size < request_size) 37 return -1; 38 else { 39 compress(ab,request_size,sum_size); 40 return 1; 41 } 42 }
这部分主要是如何去分配内存,下面的代码是在碰到还有内存时,但这些内存大小都不足以满足所要申请的大小,我们就要进行碎片整理,如果整理后还不能满足要求,我们只能对程序说声对不起。
1 //**********紧缩后分配 2 void compress(struct allocated_block *ab, int request_size, int sum_size){ 3 4 struct allocated_block *ab1, *ab2; 5 struct free_block_type *fb1, *fb2; 6 ab1 = allocated_block_head; 7 ab1->start_addr = 0; 8 ab2 = ab1->next; 9 10 while(ab2 != NULL){ 11 ab2->start_addr = ab1->start_addr + ab1->size; 12 ab1 = ab2; 13 ab2 = ab2->next; 14 } 15 printf("free block was compressed\n\n"); 16 ab->start_addr = ab1->start_addr + ab1->size; 17 18 fb1 = free_block->next; //紧缩后只有一个空闲块,无需排序 19 while(fb1 != NULL){ //其他空闲块释放 20 fb2 = fb1->next; 21 free(fb1); 22 fb1 = fb2; 23 } 24 printf("free block was set free\n\n"); 25 free_block->start_addr = ab->start_addr + ab->size; 26 free_block->size = sum_size - request_size; 27 free_block->next = NULL; 28 29 if( (sum_size >= request_size) && (sum_size - request_size < MIN_SLICE) ) { 30 ab->size = sum_size; 31 free_block->start_addr = -1; 32 free_block->size = 0; 33 } 34 }
上面就是碎片整理的代码。
下面介绍杀死一个进程并对其内存空间的释放与回收:
1 //删除进程,归还分配的存储空间,并删除描述该进程内存分配的节点 2 void kill_process(){ 3 struct allocated_block *ab; 4 int pid; 5 printf("Kill Process, pid="); 6 scanf("%d", &pid); 7 ab=find_process(pid); 8 if(ab != NULL){ 9 free_mem(ab); //释放ab所表示的分配区 10 dispose(ab); //释放ab数据结构节点 11 printf("process %2d was killed\n", pid); 12 } 13 else printf("no this process %2d\n", pid); 14 } 15 16 struct allocated_block * find_process(int pid){ 17 struct allocated_block * ab; 18 ab = allocated_block_head; 19 20 while(ab != NULL){ 21 if(ab->pid == pid) 22 return ab; 23 ab = ab->next; 24 } 25 26 return ab; 27 28 } 29 30 //将ab所表示的已分配区归还,并进行可能的合并 31 int free_mem(struct allocated_block *ab){ 32 int algorithm = ma_algorithm; 33 struct free_block_type *fbt, *pre; 34 35 fbt=(struct free_block_type*) malloc(sizeof(struct free_block_type)); 36 if(!fbt) { 37 printf("malloc free_block_type error\n"); 38 return -1; 39 } 40 fbt->start_addr = ab->start_addr; //保存结点信息并将释放结点插入到空闲队列开头 41 fbt->size = ab->size; 42 fbt->next = free_block; 43 free_block = fbt; 44 45 rearrange_FF(); //按地址有序排列 46 printf("free block was sorted according to address\n"); 47 48 pre = free_block; //合并相邻空闲分区 49 fbt = free_block->next; 50 while(fbt != NULL){ 51 if(pre->start_addr + pre->size == fbt->start_addr) 52 { 53 pre->size = pre->size + fbt->size; 54 pre->next = fbt->next; 55 free(fbt); 56 fbt = pre->next; 57 } 58 else { 59 pre = fbt->next; 60 fbt = fbt->next; 61 } 62 } 63 printf("free block combined\n\n"); 64 rearrange(ma_algorithm); //按当前算法重新排序 65 66 return 1; 67 68 } 69 70 //释放ab数据结构节点 71 int dispose(struct allocated_block *free_ab){ 72 struct allocated_block *pre, *ab; 73 74 if(free_ab == allocated_block_head) { //如果要释放第一个节点 75 allocated_block_head = allocated_block_head->next; 76 free(free_ab); 77 printf("Node ab has been free\n"); 78 return 1; 79 } 80 81 pre = allocated_block_head; //释放的是其他节点 82 ab = allocated_block_head->next; 83 84 while(ab != free_ab){ 85 pre = ab; 86 ab = ab->next; 87 } 88 pre->next = ab->next; 89 free(ab); 90 return 2; 91 }
当然这只一个对内存管理的模拟,如果在运行这个程序时,我们没释放所有内存空间,我们就要在程序结束之前,自动对其释放,要不就会造成内存泄露。
1 void do_exit(){ //释放空间 2 struct free_block_type *fb1, *fb2; 3 struct allocated_block *ab1, *ab2; 4 5 fb1 = free_block; 6 while(fb1 !=NULL){ 7 fb2 = fb1->next; 8 free(fb1); 9 fb1 = fb2; 10 } 11 printf("free_block_type free\n\n"); 12 13 ab1 = allocated_block_head; 14 while (ab1 != NULL){ 15 ab2 = ab1->next; 16 free(ab1); 17 ab1 = ab2; 18 } 19 printf("allocated_block free\n\n"); 20 }
好了,再来一个最原始的显示内存使用情况的界面吧。
1 // 显示当前内存的使用情况,包括空闲区的情况和已经分配的情况 2 int display_mem_usage(){ 3 struct free_block_type *fbt=free_block; 4 struct allocated_block *ab=allocated_block_head; 5 6 //if(fbt==NULL) return(-1); 7 8 printf("----------------------------------------------------------\n"); 9 10 // 显示空闲区 11 printf("Free Memory:\n"); 12 printf("%20s %20s\n", " start_addr", " size"); 13 while(fbt!=NULL){ 14 printf("%20d %20d\n", fbt->start_addr, fbt->size); 15 fbt=fbt->next; 16 } 17 18 // 显示已分配区 19 printf("\nUsed Memory:\n"); 20 printf("%10s %20s %10s %10s\n", "PID", "ProcessName", "start_addr", " size"); 21 22 while(ab!=NULL){ 23 printf("%10d %20s %10d %10d\n", ab->pid, ab->process_name, ab->start_addr, ab->size); 24 ab=ab->next; 25 } 26 printf("----------------------------------------------------------\n"); 27 28 return 0; 29 } 30
主函数是最简单的,也相当于一个菜单。
1 main(){ 2 char choice; 3 pid=0; 4 free_block = init_free_block(mem_size); //初始化空闲区 5 for(;;){ 6 display_menu(); //显示菜单 7 fflush(stdin); 8 choice=getchar(); //获取用户输入 9 10 switch(choice){ 11 case '1' : set_mem_size(); break; //设置内存大小 12 case '2' : set_algorithm();flag=1; break; //设置分配算法 13 case '3' : new_process(); flag=1; break; //创建新进程 14 case '4' : kill_process(); flag=1; break; //删除进程 15 case '5' : display_mem_usage(); flag=1; break; //显示内存使用 16 case '0' : do_exit(); exit(0); //释放链表并退出 17 18 default: break; 19 } 20 21 } 22 }
结束了,太长了是不,慢慢看,刚开始我也是慢慢理解了才写的,如果哪块有更好的解决办法,请各位多多指教,我会虚心接受,期待你的意见或建议。今天天气不错,艳阳高照,在初冬时节,有这样的天气,感觉很舒适,希望这样的天气保持到星期天。