内存管理(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的函数,完成对内存空间的管理工作。

时间: 2024-10-25 16:45:47

内存管理(30天自制操作系统--读书笔记)的相关文章

多定时器处理1(30天自制操作系统--读书笔记)

自认为写过很多MCU程序,但总是回头想想,我所了解的MCU编程思想大体有两种,其中具体的想法我得再找时间写下来. 总想总结出一个可扩展的,易移植的写法,但能力还没到这个层次.但<30天自制操作系统>这本书确实给我了一个思路,就像我已经写过的两篇读书笔记. 将两个独立的内容--FIFO和内存动态管理做到高度模块化,尤其是其中数据结构模型的设计更是我学习的好例子. 今天要学习的设计内容是多定时器处理.原书对这部分的处理讲的很详细,由浅入深,看得我由衷佩服作者,也可能是因为我水平低,稍稍看出点门道来

单字节的FIFO缓存(30天自制操作系统--读书笔记)

从今天起,写一些读书笔记.最近几个月都在看<30天自制操作系统这本书>,书虽说看的是电子书,但可以花钱买的正版书,既然花费了金钱,就总得有些收获. 任何人都不能总是固步自封,想要进步就得学习别人的知识,对于程序员而言,最简单的方法即是学习别人的代码. 今天的标题是“单字节的FIFO缓存”,其实就是做一个FIFO,看名字就知道了.也就4个函数和1个相关结构体,这样的小代码在嵌入式系统中很常用,也会很好用. 1.相关数据结构体 struct FIFO8 { unsigned char *buf;

多定时器处理2(30天自制操作系统 -- 读书笔记)

今天的内容依然来自<30天自制操作系统>这本书. 1.中断处理程序存在的问题,中断处理程序Timer_Interrupt如下: //定时器中断函数 void Timer_Interrupt(void) { int i=0; timerctl.count ++; for(i=0;i<MAX_TIMER;i++) //扫描所有的定时器 { if(timerctl.timer[i].flag == TIMER_FLAG_USING) { timerctl.count--; if(timerct

30天自制操作系统读书笔记(二)

我还以为马大哈的作者忘记解释之前那段好长的汇编代码了,留下这么多疑惑! 第二天作者就交代了:以后再讲,第二天我们先来谈谈程序核心部分的内容吧! 好吧,刚准备欢喜的等着作者解释,没想到他就扔了一大串代码(什么鬼?) ; hello-os ; TAB=4 ORG 0x7c00 ; 指明程序的装载地址,这个指令是和nask编译器说的(伪指令)告诉nask,开始执行的时候,把代码放到0x7c00的地方. ;为什么是0x7c00呢,因为规定了0x7c00-0x7dff是启动区的装载地址. ; 以下用于标准

30天自制操作系统读书笔记(一)

刚开始作者叫我们用二进制编辑器,手敲01代码写了个IMG(磁盘映像文件) ,并提供了一些文件 : Run.bat, install.bat这些一个批处理文件,其实就是写好了cmd指令保存到文档里面,方便以后执行. 其中run.bat里面的指令是这样的 : copy helloos.img ..\z_tools\qemu\fdimage0.bin ..\z_tools\make.exe         -C ../z_tools/qemu (稍微解释一下:第一行:copy指令把,helloos.i

30天自制操作系统读书笔记(五)

为了让程序灵活点,作者觉得把什么320X200这些数据直接写入程序,不如让程序自己获取. 所以就引入了结构体,指针.用指针直接在内存中获取这些数据 (如程序里的这句: Binfo_scrnx = (short *) 0xff4; ). 对于结构体和指针我就不细写了,有C语言基础的人都知道. 因为已经进入了32位模式,所以不能再用bios写好的中断程序给我们输出字符了,要手动了! 用像素点描出图形. 字符可以使用8X16的长方形像素点来表示,转变为16进制就是这样: static char fon

30天自制操作系统读书笔记(四)

想要在画面里画点什么东西,就需要往VRAM的内存区里写入点什么东西. 但是如何写入呢? 作者先用了汇编: _write_mem8:         ; void write_mem8(int addr, int data); MOV                  ECX,[ESP+4]               ; [ESP + 4]中存放的是地址,将其读入ECX MOV                  AL,[ESP+8]                 ; [ESP + 8]中存放的

多定时器处理3(30天自制操作系统 -- 读书笔记)

继续定时器中断处理的改进. 1.定时器中断程序Timer_Interrupt是这样的. //定时器中断函数 void Timer_Interrupt(void) { int i=0; timerctl.count ++; for(i=0;i<MAX_TIMER;i++) //扫描所有的定时器 { if(timerctl.timer[i].flag == TIMER_FLAG_USING) { timerctl.count--; if(timerctl.count == 0) //减得时间到了 {

《30天自制操作系统》笔记(07)——内存管理

<30天自制操作系统>笔记(07)--内存管理 进度回顾 上一篇中处理掉了绝大部分与CPU配置相关的东西.本篇介绍内存管理的思路和算法. 现在想想,从软件工程师的角度看,CPU也只是一个软件而已:它的功能就是加载指令.执行指令和响应中断,而响应中断也是在加载指令.执行指令.就像火车沿着一条环形铁轨前进:当中断发生时,就好像铁轨岔口处变轨了,火车就顺着另一条轨迹走了:走完之后又绕回来重新开始.决定CPU是否变轨的,就是CPU里的特定寄存器. 这是题外话,就此为止. 什么是内存管理 假设内存大小是