• 内存管理(30天自制操作系统--读书笔记)


      今天继续读书笔记,“挑战内存管理”(30天自制操作系统)。

          为什么对这块内容敢兴趣呢,因为曾经遇到这么一个问题。在STM32程序中想使用队列,可不是上篇讲的FIFO,而是使用了较大的内存空间,又想做队列的顺序存取管理。

          在这个队列里用到了malloc,动态申请内存,一开始是直接申请不到内存,后来在启动脚本里更改了设置堆的地址值,可以申请成功,但发现申请几次后,也申请不到内存。

          果然MCU级别的程序,内存这块处理起来就没有windows程序那么随心所欲了。讲了这么多,开始正题吧。

          1、相关数据结构体

    #define MEMMAN_FREES 4000 //最大可以有4000个独立的可用内存块
    
    struct FREEINFO
    {
            unsigned int addr,size; 
    };
    //这是内存管理的核心数据结构体,可用内存用其开始地址和内存大小表示。
    //申请内存就是在可用内存中找到大小合适的内存,返回起始地址给申请者,
    //同时可用内存就少了一块(这是申请内存大小恰好等于可用内存大小的情况,其他情况看后面代码)
    
    struct MEMMAN    //内存管理数据结构
    {
           int frees,maxfrees,lostsizes,losts;
           struct FREEINFO[MEMMAN_FREES];
    }
    //frees表示当前独立可用内存块个数
    //maxfrees用于观察,frees的最大值
    //losts表示申请内存失败的次数
    //lostsizes表示申请内存失败导致内存丢失的大小

         2、内存管理的初始化,也就是相关数据结构的初始化

    void memman_init(struct MEMMAN *man)
    {
           man->frees = 0;       //还没有一块可用内存
           man->maxfrees = 0;
           man->losts = 0;       //释放失败的次数为0
           man->lostsize = 0;  //释放失败的总内存大小也为0
    }

         3、获取当前所有可用内存的总大小

    unsigned int memman_total(struct MEMMAN *man)
    {
           unsigned int i,t=0;
           for(i=0;i<man->frees;i++)
          {
                  t+= man->free[i].size;    //只考虑所有可用内存占得总内存大小,不考虑内存连续(肯定有不连续的)。
          }
    
          return t;
    }

         4、内存管理关键代码1-申请内存

    unsigned int memman_alloc(struct MEMMAN* man,unsigned int size)    //返回值也就是申请到的内存的起始地址
    {
            unsigned int i,a;
            for(i=0;i<man->frees;i++)
           {
                  if(man->free[i].size > size)  //遍历所有可用内存块信息
                 {
                        a = man->free[i].addr;  //第i块可用内存满足要求
                        man->free[i].addr += size;
                        man->free[i].size -= size;  //更改第i块内用内存的信息
                        if(man->free[i].size == 0//恰好申请内存就与第i块可用内存大小一致
                        {
                                 man->frees--;      //可用内存少了一块
                                 for(;i<man->frees;i++)
                                 {
                                         man->free[i] = man->free[i+1];   //后面的可用内存信息前推
                                 }
                        }
                        return a;    //申请成功,返回指定地址
                 }
           }  
    
           return  0;        //没找到,即申请内存不成功
    }

         5、内存管理关键代码2-释放内存

    int memman_free(struct MEMMAN *man,unsigned int addr,unsigned int size)
    {
        int i,j;
    
        /*为了管理方便,可用内存块是按照addr顺序排列的,先找到应该放
        在可用内存块的哪里*/
        for(i=0;i<man->frees;i++)
        {
            if(man->free[i].addr > addr)
            {
                break;
            }
        }
    
        //实际就有多种情况了,下面一一分析
        /*  free[i-1].addr < addr < free[i].addr */
        if(i>0)
        {
            if(man->free[i-1].addr +man->free[i-1].size == addr)
            {
                //释放的内存与前面的可用内存块合在一起了
                man->free[i-1].size += size;
                if(i<man->frees)      //要考虑释放的内存与free[i]能不能合在一块
                {
                    if(addr+size == man->free[i].addr) //与后面的内存合在一块了
                    {
                                    memman->free[i-1].size += man->free[i].size;
                        //这样合并就减少一条可用内存信息
                        man->frees --;
                        for(;i<man->frees;i++)
                        {
                            man->free[i] = man->free[i+1];   //结构体迭代
                        }
                    }
                }
    
                return 0;      //释放成功
            }
        }
        
        if(i<man->frees)   //上面条件不成立,不能与前面的可用内存块信息合在一起
        {
            if(addr+size == man->free[i].addr)     //可以与后面的内存块信息合在一起
            {
                man->free[i].addr = addr;          //调整后面的可用内存块信息即可
                man->free[i].size += size;
    
                return 0;                                 //释放成功
            }
        }
    
        /*既不能合前面的归在一起,也不能和后面的归在一起*/
        if(man->frees < MEMMAN_FREES)    //只能增加一条可用信息
        {
                 for(j=man->frees;j>i;j--)
                 {
                        man->free[j] = man->free[j-1];
            }
            man->frees ++;
            if(man->maxfrees < man->frees)
              {
                   man->maxfrees = man->frees;     //更新最大值
            }
            man->free[i].addr = addr;
            man->free[i].size = size;
          
            return 0;    //释放成功
        }
    
        /*不能往后移动*/
        man->losts ++;
        man->lostsize += size;
    
        return -1;
    }

         以上代码如何使用:

    1、先初始化结构体

         struct MEMMAN *memman = (struct MEMMAN *)malloc(struct MEMMAN);

    2、调用memman_init初始化数据结构体

         memman_init(memman);

    3、先释放整个可用内存(如64K RAM,后30K设为可被动态申请内存)

         memman_free(memman,&34K,30K);

    4、在需要申请内存的地方进行内存申请

         char *buf = (char *)memman_alloc(memman,1000);              //申请1000个字节空间

    5、在任何合适地方都可获取可用内存的总大小

         unsigned int memtotal = memman_total(memman);

         以上所有源代码即完成了一个功能,自定义了类malloc和free的函数,完成对内存空间的管理工作。

  • 相关阅读:
    [WCF权限控制]模拟(Impersonation)与委托(Delegation)[上篇]
    深入剖析授权在WCF中的实现[共14篇]
    模拟在WCF中的应用
    WCF服务端运行时架构体系详解[下篇]
    WCF运行时框架的构建与扩展[共10篇]
    如何在EHAB(EntLib)中定义”细粒度”异常策略?
    WCF服务端运行时架构体系详解[续篇]
    《WCF服务编程》关于“队列服务”一个值得商榷的地方
    vs2005入门 之 结构化数据类型[一](数组)[视频]
    Atlas入门UpdateProgress(利用GridView翻页)[视频]
  • 原文地址:https://www.cnblogs.com/kanite/p/4478000.html
Copyright © 2020-2023  润新知