zoukankan      html  css  js  c++  java
  • 对操作系统内存管理的模拟(应用)

      今天来续写,觉得昨天那种安排不合理,于是将原理与应用分两篇来写,不至于让大家和我看的有些烦。

      温故而知新可以为师矣,如果不能为师,给自己当老师也不错哦。好了,写一个模拟内存管理的程序吧,老师说有原理,也要有具体实现,这是提升分析问题的一种途径。下面写写程序,并进行解说,这个程序我当时也完善了一下,其实这个程序是老师给了框架,我们实现了其中的内容。

      先写一些宏定义和全局变量

    View Code
     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内核代码的童鞋对操作系统的数据结构应该有所了解,其实也就是比我们平时用的更深入、更精巧而已,基本原理还是一样,有些只是我们没那么用过,我这个还是一般的用法。

     

    View Code
     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内核也是这样命名的,基本都能见名知意,我就不费话了。

     

    View Code
     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程序的同学可能对初始化有比较深刻的理解,因为在做一些事情之前,总是要做一些准备工作,举个最简答的例子,吃饭之前要做什么准备工作,洗手?错了,我说的不是这个。吃饭你得有筷子、碗和饭菜,这些就是初始化工作,接下来你才能吃饭呀。

    View Code
     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 }

      这个相当于菜单,就这样。

     

    View Code
     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 }

      这个其实也相当于初始化,只不过你有了选择权。

     

    View Code
      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 }

      这段是按三种排序算法进行内存整理,如果你了解这三种排序算法,就没必要看这些了,我觉得只要对算法核心思想理解了,写基本是没问题的,最大的差异可能就是效率和风格。

      

    View Code
     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 }

      这部分主要是创建线程并作一些初始化工作。

      

    View Code
     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 }

      这部分主要是如何去分配内存,下面的代码是在碰到还有内存时,但这些内存大小都不足以满足所要申请的大小,我们就要进行碎片整理,如果整理后还不能满足要求,我们只能对程序说声对不起。

     

    View Code
     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 }

      上面就是碎片整理的代码。

      下面介绍杀死一个进程并对其内存空间的释放与回收:

    View Code
     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 }

      当然这只一个对内存管理的模拟,如果在运行这个程序时,我们没释放所有内存空间,我们就要在程序结束之前,自动对其释放,要不就会造成内存泄露。

    View Code
     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 }

      

      好了,再来一个最原始的显示内存使用情况的界面吧。  

    View Code
     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

      

    主函数是最简单的,也相当于一个菜单。

    View Code
     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 }

      结束了,太长了是不,慢慢看,刚开始我也是慢慢理解了才写的,如果哪块有更好的解决办法,请各位多多指教,我会虚心接受,期待你的意见或建议。今天天气不错,艳阳高照,在初冬时节,有这样的天气,感觉很舒适,希望这样的天气保持到星期天。

  • 相关阅读:
    sqlldr、sqluldr2_w64案例
    查看oracle的sid和sevice_name
    杂记
    GAN学习
    Leetcode 第 217 场周赛
    牛客编程巅峰赛S2第4场
    SAR图像变化检测的一点想法
    Fire! UVA
    HDU
    HDU
  • 原文地址:https://www.cnblogs.com/dofi/p/2259725.html
Copyright © 2011-2022 走看看