首先问个问题,你知道如何在LCD上显示SD卡文件浏览?—–需要读取所有文件名到内存,然后才能显示到LCD上。

一般的方法:是定义一个数组来存储文件名

1:需要知道最大文件名的长度

2:需要知道文件的个数。100?1000?10000?

倘若没有内存管理的话,我们得定义一个uint8_t fileNametBL【10000】【255】的数组!你想想会占用多少的内存呢?—–2550k字节内存(如果实际文件大小没有2550k字节,则会大大造成浪费!)

一、内存管理简介

内存管理,是指软件运行时对MCU内存资源的分配使用的技术。其最主要的目的是:如何高效、快速的分配,并且在适当的时候释放和回收内存资源**

内存使用三部曲:内存申请(分配)

内存使用

内存释放

内存管理的实现方式有很多种,最终都是实现2个函数:malloc[用于内存的申请]和free[用于内存的释放]

标准的C语言库也提供了mallow函数和free函数来实现动态申请和释放内存。那为啥我们不用C语言库的呢?

原因如下:

  • 占用大量的代码空间 不适合在资源紧缺的嵌入式系统当中

  • 没有线程安全的相关机制

  • 运行有不确定性,每次调用这些函数时花费的时间可能都不一样

  • 内存碎片化

  • 所以我们接下来会学习一种内存管理叫—-“分块式内存管理”

    二、分块式内存管理

    (一) 介绍

    分块式内存管理由内存池和内存管理表两部分组成。内存池被等分为n块,对应的内存管理表,大小也为n,内存管理表的每一个项对应内存池的一个内存。

    内存管理表的项值代表的意义:

    当该项值为0时,代表对应的内存块未被占用。

    当该项值为非零时,代表该项对应的内存块已经被占用,其数值则代表被连续占用的内存块数。

    当内存管理初始化时,内存管理表全部清零,表示没有任何内存块被占用

    1.分配方向

    从顶——>底的分配方向:即先从最末端开始找空内存。

    2.分配原理:

    当指针p调用malloc申请内存时:

    1.先判断p要分配的内存块数(m)

    2.从第n项开始,向下查找,直到找到有m块连续的空内存块(即对应内存管理表项为0)

    3.将这m个内存管理表项的值都设置为m(标记为占用)

    4.把最后的这个空内存块的地址返回指针p,完成一次分配

    注:若当内存不够时(就是找到最后也没有找到连续空闲m块空闲内存),则返回MULL给p,表示分配失败!

    3.释放原理

    当指针p申请的内存用完,需要释放的时候,调用free函数实现。

    free函数实现:

    1.判断p指向的内存地址所对应的内存块

    2.找到对应的内存管理项目,得到p所占用的内存块数目m

    3.将这m个内存管理项目的值都清零,标记释放,完成一次内存释放

    4.管理内存情况(基于STM32F429):

    内存池大小:160*1024 B

    内存块大小:64B

    内存块数目 = 管理表项数目 =内存池大小/内存块大小= 160 * 1024 / 64

    管理表项大小:2B(当申请内存后,内存管理表会做标记,最多是5120块,用2B就够了,,因为5120<65535)

    占用内存大小=内存池+内存管理表

    内存池占用的内存大小=内存块大小 * 内存块数

    (二)内存管理使用

    1.内存管理控制器结构体

    这里有重要的内存管理控制器结构体和内存表参数宏定义

    /* mem1 内存池1参数设定.mem1 完全处于F429内部 SRAM (最大192k)里面. */
    #define MEM1_BLOCK_SIZE 64 /* 内存块大小为 64 字节 */
    #define MEM1_MAX_SIZE 160 * 1024 /* 最大管理内存 160K */
    #define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLOCK_SIZE /* 内存池1的内存表大小 */
     
    /* mem2 内存池2参数设定.mem2 处于 CCM,用于管理 CCM(特别注意,这部分 SRAM,仅 CPU 可以访问!!) */
    #define MEM2_BLOCK_SIZE 64 /* 内存块大小为 64 字节 */
    #define MEM2_MAX_SIZE 60 * 1024 /* 最大管理内存 60K */
    #define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE/MEM2_BLOCK_SIZE /* 内存池2的内存表大小 */
     
    /* mem3 内存池3参数设定.mem3的内存池处于外部 SDRAM 里面 */
    #define MEM3_BLOCK_SIZE 64 /* 内存块大小为 64 字节 */
    #define MEM3_MAX_SIZE 28912 * 1024 /* 最大管理内存 28912K */
    #define MEM3_ALLOC_TABLE_SIZE MEM3_MAX_SIZE/MEM3_BLOCK_SIZE /* 内存池3的内存表大小 */
    ​
    ​
    /* 内存池(32 字节对齐) */
    static __align(32) uint8_t mem1base[MEM1_MAX_SIZE]; /* 内部 SRAM 内存池 */ 
    static __align(32) uint8_t mem2base[MEM2_MAX_SIZE]
    __attribute__((at(0X10000000))); /* 内部 CCM 内存池 */
    static __align(32) uint8_t mem3base[MEM3_MAX_SIZE]
    __attribute__((at(0XC01F4000)));
    /* 外部 SDRAM 内存池,前面 2M 给 LTDC 用了(1280*800*2) */
    /* 内存管理表 */
    static MT_TYPE mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; /* 内部 SRAM 内存池 MAP */
    static MT_TYPE mem2mapbase[MEM2_ALLOC_TABLE_SIZE]
     __attribute__((at(0X10000000 + MEM2_MAX_SIZE))); /* 内部 CCM 内存池 MAP */
    static MT_TYPE mem3mapbase[MEM3_ALLOC_TABLE_SIZE]
     __attribute__((at(0XC01F4000+ MEM3_MAX_SIZE))); /* 外部 SRAM 内存池 MAP */
    #else /* 使用 AC6 编译器时 */
    /* 内存池(32 字节对齐) */
    static __ALIGNED(32) uint8_t mem1base[MEM1_MAX_SIZE]; 
    /* 内部 SRAM 内存池 */
    static __ALIGNED(32) uint8_t mem2base[MEM2_MAX_SIZE]
    __attribute__((section(".bss.ARM.__at_0X10000000"))); 
    /* 内部 CCM 内存池 */
    static __ALIGNED(32) uint8_t mem3base[MEM3_MAX_SIZE]
    __attribute__((section(".bss.ARM.__at_0XC01F4000")));
    /* 外部 SDRAM 内存池,前面 2M 给 LTDC 用了(1280*800*2) */
    /* 内存管理表 */
    static MT_TYPE mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; /* 内部 SRAM 内存池 MAP */
    static MT_TYPE mem2mapbase[MEM2_ALLOC_TABLE_SIZE]
    __attribute__((section(".bss.ARM.__at_0X1000F000"))); /* 内部 CCM 内存池 MAP */
    static MT_TYPE mem3mapbase[MEM3_ALLOC_TABLE_SIZE]
    __attribute__((section(".bss.ARM.__at_0XC1E30000"))); /* 外部 SRAM 内存池 MAP */
    #endif
    /* 内存管理参数 */
    const uint32_t memtblsize[SRAMBANK] = {MEM1_ALLOC_TABLE_SIZE,
    MEM2_ALLOC_TABLE_SIZE, MEM3_ALLOC_TABLE_SIZE}; /* 内存表大小 */
    const uint32_t memblksize[SRAMBANK] = {MEM1_BLOCK_SIZE, MEM2_BLOCK_SIZE,
    MEM3_BLOCK_SIZE}; /* 内存分块大小 */
    /* 内存总大小 */
    const uint32_t memsize[SRAMBANK]={MEM1_MAX_SIZE, MEM2_MAX_SIZE, MEM3_MAX_SIZE}; 
    /* 内存管理控制器 */
    struct _m_mallco_dev mallco_dev=
    {
      my_mem_init, /* 内存初始化 */
      my_mem_perused, /* 内存使用率 */
      mem1base,mem2base,mem3base, /* 内存池 */
      mem1mapbase,mem2mapbase,mem3mapbase, /* 内存管理状态表 */
      0, 0, 0, /* 内存管理未就绪 */
    };
    2.初始化内存
    void my_mem_init(uint8_t memx)  
    {  
        my_mem_set(mallco_dev.memmap[memx], 0, memtblsize[memx] * 4);  /* 内存状态表数据清零 */
        mallco_dev.memrdy[memx] = 1;                                   /* 内存管理初始化OK */
    }

    其中

    void my_mem_set(void *s, uint8_t c, uint32_t count)  
    {  
        uint8_t *xs = s;  
        while (count--)
            *xs++ = c;  
    }  

    获取内存使用率

    /**
     * @brief       获取内存使用率
     * @param       memx : 所属内存块
     * @retval      使用率(扩大了10倍,0~1000,代表0.0%~100.0%)
     */
    uint16_t my_mem_perused(uint8_t memx)  
    {  
        uint32_t used = 0;  
        uint32_t i;
    ​
        for (i = 0; i < memtblsize[memx]; i++)  
        {
            if (mallco_dev.memmap[memx][i])
            {
                used++;
            }
        }
    ​
        return (used * 1000) / (memtblsize[memx]);  
    }
    3.申请内存

    /**
     * @brief       分配内存(外部调用)
     * @param       memx : 所属内存块
     * @param       size : 要分配的内存大小(字节)
     * @retval      分配到的内存首地址.
     */
    void *mymalloc(uint8_t memx, uint32_t size)
    {
        uint32_t offset;
        offset = my_mem_malloc(memx, size);//得到偏移地址
    ​
        if (offset == 0xFFFFFFFF)   /* 申请出错 */
        {
            return NULL;            /* 返回空(0) */
        }
        else                        /* 申请没问题, 返回首地址 */
        {
            return (void *)((uint32_t)mallco_dev.membase[memx] + offset);//返回的是内存的首地址
        }
    }

    其中

    /**
     * @brief       内存分配(内部调用)
     * @param       memx : 所属内存块
     * @param       size : 要分配的内存大小(字节)
     * @retval      内存偏移地址
     *   @arg       0 ~ 0xFFFFFFFE : 有效的内存偏移地址
     *   @arg       0xFFFFFFFF     : 无效的内存偏移地址
     */
    uint32_t my_mem_malloc(uint8_t memx, uint32_t size)  
    {  
        signed long offset = 0;  
        uint32_t nmemb;                                             /* 需要的内存块数 */
        uint32_t cmemb = 0;                                         /* 连续空内存块数 */
        uint32_t i;
    ​
        if (!mallco_dev.memrdy[memx])
        {
            mallco_dev.init(memx);                                  /* 未初始化,先执行初始化 */
        }
        if (size == 0) 
        {
            return 0XFFFFFFFF;                           /* 不需要分配 */
        }
        nmemb = size / memblksize[memx];                            /* 获取需要分配的连续内存块数 */
    ​
        if (size % memblksize[memx])
        {
            nmemb++;
        }
    ​
        for (offset = memtblsize[memx] - 1; offset >= 0; offset--)   /* 搜索整个内存控制区 */
        {
            if (!mallco_dev.memmap[memx][offset])
            {
                cmemb++;                                            /* 连续空内存块数增加 */
            }
            else 
            {
                cmemb = 0;                                          /* 连续内存块清零 */
            }
    ​
            if (cmemb == nmemb)                                     /* 找到了连续nmemb个空内存块 */
            {
                for (i = 0;i < nmemb; i++)                          /* 标注内存块非空  */
                {  
                    mallco_dev.memmap[memx][offset + i] = nmemb;  
                }
    ​
                return (offset * memblksize[memx]);                 /* 返回偏移地址  */
            }
        }
    ​
        return 0XFFFFFFFF;                                          /* 未找到符合分配条件的内存块 */
    }

    4.操作内存
    /**
     * @brief       复制内存
     * @param       *des : 目的地址
     * @param       *src : 源地址
     * @param       n    : 需要复制的内存长度(字节为单位)
     * @retval      无
     */
    void my_mem_copy(void *des, void *src, uint32_t n)  
    {  
        uint8_t *xdes = des;
        uint8_t *xsrc = src; 
        while (n--)*xdes++ = *xsrc++;  
    }  
    ​
    /**
     * @brief       设置内存值
     * @param       *s    : 内存首地址
     * @param       c     : 要设置的值
     * @param       count : 需要设置的内存大小(字节为单位)
     * @retval      无
     */
    void my_mem_set(void *s, uint8_t c, uint32_t count)  
    {  
        uint8_t *xs = s;  
        while (count--)*xs++ = c;  
    }  
    ​

    5.释放内存
    /**
     * @brief       释放内存(内部调用)
     * @param       memx   : 所属内存块
     * @param       offset : 内存地址偏移
     * @retval      释放结果
     *   @arg       0, 释放成功;
     *   @arg       1, 释放失败;
     *   @arg       2, 超区域了(失败);
     */
    uint8_t my_mem_free(uint8_t memx, uint32_t offset)
    {
        int i;
    ​
        if (!mallco_dev.memrdy[memx])                   /* 未初始化,先执行初始化 */
        {
            mallco_dev.init(memx);
            return 1;                                   /* 未初始化 */
        }
        //通过offset确定偏移在第几个内存块以及获取内存块数
        if (offset < memsize[memx])                     /* 偏移在内存池内. */
        {
            int index = offset / memblksize[memx];      /* 偏移所在内存块号码 */
            int nmemb = mallco_dev.memmap[memx][index]; /* 内存块数量 */
    ​
            for (i = 0; i < nmemb; i++)                 /* 对已经占用的内存块清零 */
            {
                mallco_dev.memmap[memx][index + i] = 0;
            }
    ​
            return 0;
        }
        else
        {
            return 2;                                  /* 偏移超区了. */
        }
    }

    作者:菲子叭叭

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【STM32】内存管理

    发表回复