当前位置: 首页 > news >正文

STM32F4 内存管理介绍及应用

第十八章 内存管理介绍及应用

1. 内存管理简介

内存管理,是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效、快速的分配,并且在适当的时候释放和回收内存资源。内存管理的实现方法有很多种,其实最终都是要实现两个函数: malloc 和 free。 malloc 函数用来内存申请, free 函数用于内存释放。

本章,我们介绍一种比较简单的办法来实现:分块式内存管理。下面我们介绍一下该方法的实现原理

在这里插入图片描述

从上图可以看出,分块式内存管理由内存池和内存管理表两部分组成。内存池被等分为了n 块,对应的内存管理表,大小也为 n,内存管理表的每一个项对应内存池的一块内存。

内存管理表的项值代表的意义为:当该项值为 0 的时候,代表对应的内存块未被占用,当该项值非零的时候,代表该项对应的内存块已经被占用,其数值则代表被连续占用的内存块数。比如某项值为 10,那么说明包括本项对应的内存块在内,总共分配了 10 个内存块给外部的某个指针。

内存分配方向如上图所示,是从顶→底的分配方向。即首先从最末端开始找空内存。当内存管理刚初始化的时候,内存表全部清零,表示没有任何内存块被占用。

1.1 分配原理

当指针 p 调用 malloc 申请内存的时候,先判断 p 要分配的内存块数(m),然后从第 n 开始,向下查找,直到找到 m 块连续的空内存块(即对应内存管理表项为 0),然后将这 m 个内存管理表项的值都设置为 m(标记被占用),最后,把最后的这个空内存块的地址返回指针 p,完成一次分配。注意:如果当内存不够的时候(找到最后也没有找到连续 m 块空闲内存),则返回 NULL 给 p,表示分配失败。

1.2 释放原理

当 p 申请的内存用完,需要释放的时候,调用 free 函数实现。 free 函数先判断 p 指向的内存地址所对应的内存块,然后找到对应的内存管理表项目,得到 p 所占用的内存块数目 m(内存管理表项目的值就是所分配内存块的数目),将这 m 个内存管理表项目的值都清零,标记释放,完成一次内存释放。

2. 内存管理应用示例

2.1 相关宏定义

#ifndef __MALLOC_H
#define __MALLOC_H#include "sys.h"/* 定义3个内存池 */
#define SRAMIN     0  // 内部内存池 
#define SRAMCCM    1  // CCM内存池(此部分SRAM仅仅CPU可以访问!!!) 
#define SRAMEX     2  // 外部内存池
#define SRAMBANK   3  // 定义支持的SRAM块数/* 定义内存管理表类型,当外扩SDRAM的时候,必须使用uint32_t类型,否则可以定义成uint16_t,以节省内存占用 */
#define MT_TYPE  uint16_t/* 单块内存,内存管理所占用的全部空间大小计算公式如下:* size = MEM1_MAX_SIZE + (MEM1_MAX_SIZE / MEM1_BLOCK_SIZE) * sizeof(MT_TYPE)* 以SRAMEX为例,size = 963 * 1024 + (963 * 1024 / 32) * 2 = 1047744 ≈ 1023KB* 已知总内存容量(size),最大内存池的计算公式如下:* MEM1_MAX_SIZE = (MEM1_BLOCK_SIZE * size) / (MEM1_BLOCK_SIZE + sizeof(MT_TYPE))* 以CCM为例, MEM2_MAX_SIZE = (32 * 64) / (32 + 2) = 60.24KB ≈ 60KB*//* mem1内存参数设定.mem1完全处于内部SRAM里面 */
#define MEM1_BLOCK_SIZE         32                              // 内存块大小为32字节
#define MEM1_MAX_SIZE           100 * 1024                      // 最大管理内存 100K
#define MEM1_ALLOC_TABLE_SIZE   MEM1_MAX_SIZE/MEM1_BLOCK_SIZE   // 内存表大小
/* mem2内存参数设定.mem2处于CCM,用于管理CCM(特别注意,这部分SRAM,仅CPU可以访问!!) */
#define MEM2_BLOCK_SIZE         32                              // 内存块大小为32字节 
#define MEM2_MAX_SIZE           60 * 1024                       // 最大管理内存60K 
#define MEM2_ALLOC_TABLE_SIZE   MEM2_MAX_SIZE/MEM2_BLOCK_SIZE   // 内存表大小 
/* mem3内存参数设定.mem3是外扩SRAM */
#define MEM3_BLOCK_SIZE         32                              // 内存块大小为32字节 
#define MEM3_MAX_SIZE           963 * 1024                      // 最大管理内存963K 
#define MEM3_ALLOC_TABLE_SIZE   MEM3_MAX_SIZE/MEM3_BLOCK_SIZE   // 内存表大小 /* 如果没有定义NULL, 定义NULL */
#ifndef NULL
#define NULL 0
#endif/* 内存管理控制器 */
struct _m_mallco_dev
{void (*init)(uint8_t);        // 初始化 uint16_t (*perused)(uint8_t); // 内存使用率 uint8_t *membase[SRAMBANK];   // 内存池 管理SRAMBANK个区域的内存 MT_TYPE *memmap[SRAMBANK];    // 内存管理状态表 uint8_t  memrdy[SRAMBANK];    // 内存管理是否就绪 
};extern struct _m_mallco_dev mallco_dev;
void my_mem_init(uint8_t memx);                          // 内存管理初始化函数(外/内部调用) 
uint16_t my_mem_perused(uint8_t memx) ;                  // 获得内存使用率(外/内部调用) 
void my_mem_set(void *s, uint8_t c, uint32_t count);     // 内存设置函数 
void my_mem_copy(void *des, void *src, uint32_t n);      // 内存拷贝函数 
void myfree(uint8_t memx, void *ptr);                    // 内存释放(外部调用) 
void *mymalloc(uint8_t memx, uint32_t size);             // 内存分配(外部调用) 
void *myrealloc(uint8_t memx, void *ptr, uint32_t size); // 重新分配内存(外部调用) #endif /* __MALLOC_H */

2.2 AC5/6编译器兼容

#if !(__ARMCC_VERSION >= 6010050)   /* 不是AC6编译器,即使用AC5编译器时 */
/* 内存池(64字节对齐) */
static __align(64) uint8_t mem1base[MEM1_MAX_SIZE];                                     /* 内部SRAM内存池 */
static __align(64) uint8_t mem2base[MEM2_MAX_SIZE] __attribute__((at(0x10000000)));     /* 内部CCM内存池 */
static __align(64) uint8_t mem3base[MEM3_MAX_SIZE] __attribute__((at(0x68000000)));     /* 外部SRAM内存池 *//* 内存管理表 */
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(0x68000000 + MEM3_MAX_SIZE)));  /* 外部SRAM内存池MAP */
#else      /* 使用AC6编译器时 */
/* 内存池(64字节对齐) */
static __ALIGNED(64) uint8_t mem1base[MEM1_MAX_SIZE];                                                           /* 内部SRAM内存池 */
static __ALIGNED(64) uint8_t mem2base[MEM2_MAX_SIZE] __attribute__((section(".bss.ARM.__at_0x10000000")));      /* 内部CCM内存池 */
static __ALIGNED(64) uint8_t mem3base[MEM3_MAX_SIZE] __attribute__((section(".bss.ARM.__at_0x68000000")));      /* 外部SRAM内存池 */ 
/* 内存管理表 */
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_0x680F0C00")));         /* 外部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};     

2.3 核心操作函数

/* 内存管理控制器 */
struct _m_mallco_dev mallco_dev =
{my_mem_init,                                /* 内存初始化 */my_mem_perused,                             /* 内存使用率 */mem1base, mem2base, mem3base,               /* 内存池 */mem1mapbase, mem2mapbase, mem3mapbase,      /* 内存管理状态表 */0, 0, 0,                                    /* 内存管理未就绪 */
};/*** @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;
}/*** @brief       内存管理初始化* @param       memx : 所属内存块* @retval      无*/
void my_mem_init(uint8_t memx)
{uint8_t mttsize = sizeof(MT_TYPE);  /* 获取memmap数组的类型长度(uint16_t /uint32_t)*/my_mem_set(mallco_dev.memmap[memx], 0, memtblsize[memx] * mttsize); /* 内存状态表数据清零 */mallco_dev.memrdy[memx] = 1;        /* 内存管理初始化OK */
}/*** @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]);
}/*** @brief       内存分配(内部调用)* @param       memx : 所属内存块* @param       size : 要分配的内存大小(字节)* @retval      内存偏移地址*   @arg       0 ~ 0xFFFFFFFE : 有效的内存偏移地址*   @arg       0xFFFFFFFF     : 无效的内存偏移地址*/
static 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;                  /* 未找到符合分配条件的内存块 */
}/*** @brief       释放内存(内部调用)* @param       memx   : 所属内存块* @param       offset : 内存地址偏移* @retval      释放结果*   @arg       0, 释放成功;*   @arg       1, 释放失败;*   @arg       2, 超区域了(失败);*/
static uint8_t my_mem_free(uint8_t memx, uint32_t offset)
{int i;if (!mallco_dev.memrdy[memx])   /* 未初始化,先执行初始化 */{mallco_dev.init(memx);return 1;                   /* 未初始化 */}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;   /* 偏移超区了 */}
}/*** @brief       释放内存(外部调用)* @param       memx : 所属内存块* @param       ptr  : 内存首地址* @retval      无*/
void myfree(uint8_t memx, void *ptr)
{uint32_t offset;if (ptr == NULL) return;    /* 地址为0. */offset = (uint32_t)ptr - (uint32_t)mallco_dev.membase[memx];my_mem_free(memx, offset);  /* 释放内存 */
}/*** @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       *ptr : 旧内存首地址* @param       size : 要分配的内存大小(字节)* @retval      新分配到的内存首地址.*/
void *myrealloc(uint8_t memx, void *ptr, uint32_t size)
{uint32_t offset;offset = my_mem_malloc(memx, size);if (offset == 0xFFFFFFFF)   /* 申请出错 */{return NULL;            /* 返回空(0) */}else                        /* 申请没问题, 返回首地址 */{my_mem_copy((void *)((uint32_t)mallco_dev.membase[memx] + offset), ptr, size);  /* 拷贝旧内存内容到新内存 */myfree(memx, ptr);      /* 释放旧内存 */return (void *)((uint32_t)mallco_dev.membase[memx] + offset);                   /* 返回新内存首地址 */}
}

2.4 主函数测试

#include "bsp_init.h"
#include "stdio.h"
#include "SerialInvoker.h"
#include "sram.h"
#include "malloc.h"const char *SRAM_NAME_BUF[SRAMBANK] = {" SRAMIN  ", " SRAMCCM ", " SRAMEX  "};int main(void)
{uint8_t paddr[20]; // 存放P Addr:+p地址的ASCII值uint16_t memused = 0; // 记录已用SRAM大小uint8_t key_value = 0;uint8_t i = 0;uint8_t *p = 0;uint8_t *tp = 0;uint8_t sramx = 0; // 默认为内部SRAMbsp_init();sram_init();serial_invoker_init(84);my_mem_init(SRAMIN); // 初始化内部SRAMmy_mem_init(SRAMCCM); // 初始化CCM SRAMmy_mem_init(SRAMEX); // 初始化外部SRAMLCD_ShowString(30,110,200,16,16,"KEY0:Malloc & WR & Show");LCD_ShowString(30,130,200,16,16,"KEY_UP:SRAMx KEY1:Free");LCD_ShowString(60,160,200,16,16," SRAMIN ");LCD_ShowString(30,176,200,16,16,"SRAMIN   USED:");LCD_ShowString(30,192,200,16,16,"SRAMCCM  USED:");LCD_ShowString(30,208,200,16,16,"SRAMEX   USED:");while(1){key_value = key_scan(0);switch(key_value){case KEY0_Press:p = mymalloc(sramx, 2048); // 按下KEY0申请2KB空间if(p!= NULL){sprintf((char *)p, "Memory Malloc Test%03d", i); // 写入数据LCD_ShowString(30,260,209,16,16,(char*)p);}break;case KEY1_Press:myfree(sramx, p); // 按下KEY1释放申请的空间p = 0;break;case WKUP_Press:sramx++;if(sramx > SRAMBANK) sramx = 0;LCD_ShowString(60,160,200,16,16,(char*)SRAM_NAME_BUF[sramx]);break;}if(tp != p){tp = p;sprintf((char *)paddr, "P Addr:0X%08X", (uint32_t)tp);LCD_ShowString(30,240,209,16,16,(char*)paddr);if(p){LCD_ShowString(30,260,280,16,16,(char*)p);}else{LCD_Fill(30,260,280,200,WHITE);}}delay_ms(10);i++;if((i % 20) == 0){memused = my_mem_perused(SRAMIN); // 获取内存使用率sprintf((char*)paddr, "%d.%01d%%", memused / 10, memused % 10);LCD_ShowString(30+112,176,200,16,16,(char*)paddr);memused = my_mem_perused(SRAMCCM); // 获取内存使用率sprintf((char*)paddr, "%d.%01d%%", memused / 10, memused % 10);LCD_ShowString(30+112,192,200,16,16,(char*)paddr);memused = my_mem_perused(SRAMEX); // 获取内存使用率sprintf((char*)paddr, "%d.%01d%%", memused / 10, memused % 10);LCD_ShowString(30+112,208,200,16,16,(char*)paddr);LED_TOGGLE(LED0_GPIO_Pin);}}
}

3. malloc API总结

3.1 关键宏定义

  • 内存区域索引:
    • SRAMIN: 0 (内部 SRAM)
    • SRAMCCM: 1 (CCM SRAM)
    • SRAMEX: 2 (外部 SRAM)
    • SRAMBANK: 3 (支持的内存区域总数)
  • 内存块配置:
    • MEM1_BLOCK_SIZE: 内部 SRAM 内存块大小 (32 字节)
    • MEM2_BLOCK_SIZE: CCM SRAM 内存块大小 (32 字节)
    • MEM3_BLOCK_SIZE: 外部 SRAM 内存块大小 (32 字节)
  • 内存池大小配置:
    • MEM1_MAX_SIZE: 内部 SRAM 最大管理内存 (100KB)
    • MEM2_MAX_SIZE: CCM SRAM 最大管理内存 (60KB)
    • MEM3_MAX_SIZE: 外部 SRAM 最大管理内存 (963KB)
  • 内存管理表大小配置:
    • MEM1_ALLOC_TABLE_SIZE: 内部 SRAM 内存管理表条目数
    • MEM2_ALLOC_TABLE_SIZE: CCM SRAM 内存管理表条目数
    • MEM3_ALLOC_TABLE_SIZE: 外部 SRAM 内存管理表条目数
  • 管理表类型:
    • MT_TYPE: 内存管理表条目类型 (uint16_t)

3.2 结构体

  • struct _m_mallco_dev mallco_dev (全局变量):
    这是一个全局的内存管理控制器结构体,用户通常不需要直接访问其内部成员,而是通过其提供的函数指针和全局的 mymallocmyfree 等函数进行操作。

3.3 函数

  • void my_mem_init(uint8_t memx)

    • 功能: 初始化指定内存区域的内存管理系统。在使用任何内存分配函数之前,必须先对相应的内存区域进行初始化。
    • 参数:
      • memx: 内存区域索引。可以是 SRAMIN, SRAMCCM, 或 SRAMEX
    • 返回值: 无。
    • 备注: mymalloc 函数在检测到未初始化时会自动调用此函数,但显式调用可以确保在第一次分配前就绪。
  • uint16_t my_mem_perused(uint8_t memx)

    • 功能: 获取指定内存区域的内存使用率。
    • 参数:
      • memx: 内存区域索引。
    • 返回值: 内存使用率,范围为 0 到 1000(代表 0.0% 到 100.0%)。
  • void *mymalloc(uint8_t memx, uint32_t size)

    • 功能: 在指定内存区域分配指定大小的内存。
    • 参数:
      • memx: 内存区域索引。
      • size: 要分配的内存大小,单位为字节。
    • 返回值:
      • 成功:分配到的内存块的首地址指针。
      • 失败(如内存不足):NULL
  • void myfree(uint8_t memx, void *ptr)

    • 功能: 释放指定内存区域中之前分配的内存。
    • 参数:
      • memx: 内存区域索引。
      • ptr: 要释放的内存块的首地址指针。
    • 返回值: 无。
    • 备注: 释放 NULL 指针是安全的,函数会忽略。
  • void *myrealloc(uint8_t memx, void *ptr, uint32_t size)

    • 功能: 重新分配指定内存区域中已分配的内存块,改变其大小。
    • 参数:
      • memx: 内存区域索引。
      • ptr: 现有内存块的首地址指针(如果为 NULL,则行为类似于 mymalloc)。
      • size: 新的内存块大小,单位为字节。
    • 返回值:
      • 成功:新分配到的内存块的首地址指针(可能与旧地址不同)。
      • 失败(如内存不足):NULL(此时原内存块保持不变)。
    • 备注: 如果新大小小于旧大小,数据会被截断;如果新大小大于旧大小,多余的部分内容未定义。

3.4 硬件/环境要求

  • STM32F4 微控制器: 程序是为 STM32F4 系列设计的,利用了其内部 SRAM、CCM SRAM 和 FSMC 外设来访问外部 SRAM。
  • 外部 SRAM: 如果使用 SRAMEX (外部内存池),需要确保硬件上已连接外部 SRAM,并且 STM32 的 FSMC/FMC 已正确配置以访问该 SRAM。
  • 编译器:
    • AC5 (ARM Compiler 5): 能够直接解析 __attribute__((at(address))) 表达式。
    • AC6 (ARM Compiler 6): 必须通过**链接器脚本 (.sct 文件)**显式定义外部 SRAM 区域,并将 mem3basemem3mapbase 和其他需要放置在外部 SRAM 的变量(例如 test_data)分配到这些区域。C 代码中的 __attribute__((section(".bss.ARM.__at_address"))) 仅作提示,最终由链接器脚本决定。

3.4 基本使用流程

  1. 初始化内存管理:
    在你的 main 函数或其他初始化代码中,根据需要初始化对应的内存池。如果程序会用到所有三个内存池,则对每个池都调用一次 my_mem_init
// main.c
bsp_init(); // 硬件初始化
sram_init(); // 外部SRAM FSMC初始化// 初始化外部SRAM内存池
my_mem_init(SRAMEX);
// 如果也使用内部SRAM内存池和CCM内存池,也需要初始化
// my_mem_init(SRAMIN);
// my_mem_init(SRAMCCM);
  1. 分配内存:
    使用 mymalloc 函数从指定内存池分配内存。
uint8_t *buffer1;
uint32_t *buffer2;// 从外部SRAM分配1KB (1024字节)
buffer1 = (uint8_t *)mymalloc(SRAMEX, 1024);
if (buffer1 != NULL) {// 内存分配成功,可以使用 buffer1// ...
} else {// 内存分配失败// ...
}// 从CCM SRAM分配50个uint32_t (200字节)
buffer2 = (uint32_t *)mymalloc(SRAMCCM, 50 * sizeof(uint32_t));
if (buffer2 != NULL) {// ...
}
  1. 使用内存:
if (buffer1 != NULL) {for (int i = 0; i < 1024; i++) {buffer1[i] = i % 256;}
}
  1. 释放内存:
if (buffer1 != NULL) {myfree(SRAMEX, buffer1);buffer1 = NULL; // 养成良好习惯,释放后置为NULL
}
if (buffer2 != NULL) {myfree(SRAMCCM, buffer2);buffer2 = NULL;
}
  1. 重新分配内存 (可选):
uint8_t *realloc_buffer = (uint8_t *)mymalloc(SRAMEX, 100);
// ... 使用 realloc_buffer ...// 将 realloc_buffer 的大小调整为 200 字节
realloc_buffer = (uint8_t *)myrealloc(SRAMEX, realloc_buffer, 200);
if (realloc_buffer != NULL) {// 重新分配成功// ...
} else {// 重新分配失败,原内存块仍有效
}
  1. 获内存使用率 (可选):
uint16_t percent_used = my_mem_perused(SRAMEX);
// 例如,如果 percent_used 是 500,表示 50.0% 使用率

4. 内存管理相关函数(HAL库及自定义实现)

4.1 HAL 库内置内存管理

4.1.1 堆栈管理 (启动文件配置)
/* 在启动文件 (startup_stm32f4xx.s) 中定义 */
Stack_Size EQU 0x800 ; 2KB 栈空间
Heap_Size EQU 0x800 ; 2KB 堆空间
4.1.2 内存分配函数
// 标准C库函数 (HAL默认使用)
void *malloc(size_t size); // 动态内存分配
void free(void *ptr); // 释放内存
void *calloc(size_t num, size_t size); // 分配并清零
void *realloc(void *ptr, size_t size); // 重新分配

4.2 自定义内存管理方案

4.2.1 分块内存管理 (Memory Pool)
// 内存池结构体
typedef struct {uint32_t *memStart;       // 内存池起始地址uint32_t memSize;         // 内存池总大小uint32_t blockSize;       // 每个块大小uint32_t blockCount;      // 块总数uint8_t *memMap;          // 内存块状态表
} MemPool_t;// 初始化内存池
void MemPool_Init(MemPool_t *pool, void *memAddr, uint32_t size, uint32_t blockSize) {pool->memStart = (uint32_t*)memAddr;pool->memSize = size;pool->blockSize = blockSize;pool->blockCount = size / blockSize;// 分配状态表 (1字节管理1块)pool->memMap = (uint8_t*)malloc(pool->blockCount);memset(pool->memMap, 0, pool->blockCount); // 全部标记为空闲
}// 分配内存块
void* MemPool_Alloc(MemPool_t *pool) {for(uint32_t i = 0; i < pool->blockCount; i++) {if(pool->memMap[i] == 0) { // 找到空闲块pool->memMap[i] = 1;   // 标记已用return (void*)((uint32_t)pool->memStart + i * pool->blockSize);}}return NULL; // 无可用空间
}// 释放内存块
void MemPool_Free(MemPool_t *pool, void *ptr) {uint32_t offset = (uint32_t)ptr - (uint32_t)pool->memStart;uint32_t index = offset / pool->blockSize;if(index < pool->blockCount) {pool->memMap[index] = 0; // 标记为空闲}
}
4.2.2 链表式内存管理
// 内存块控制结构
typedef struct MemBlock {struct MemBlock *next;  // 下一个空闲块size_t size;            // 块大小 (包含控制头)uint8_t used;           // 使用标志
} MemBlock_t;// 内存池初始化
#define MEM_POOL_SIZE 4096
static uint8_t memPool[MEM_POOL_SIZE];void Mem_Init(void) {MemBlock_t *block = (MemBlock_t*)memPool;block->size = MEM_POOL_SIZE;block->used = 0;block->next = NULL;
}// 内存分配
void* Mem_Alloc(size_t size) {// 对齐要求 (4字节对齐)size = (size + 3) & ~3;MemBlock_t *best = NULL;MemBlock_t **prev = &((MemBlock_t*)memPool)->next;for(MemBlock_t *curr = (MemBlock_t*)memPool; curr; prev = &curr->next, curr = curr->next) {if(!curr->used && curr->size >= size + sizeof(MemBlock_t)) {if(!best || curr->size < best->size) {best = curr;}}}if(!best) return NULL; // 内存不足// 分割块 (如果剩余空间足够)if(best->size - size > sizeof(MemBlock_t) + 4) {MemBlock_t *newBlock = (MemBlock_t*)((uint8_t*)best + sizeof(MemBlock_t) + size);newBlock->size = best->size - size - sizeof(MemBlock_t);newBlock->used = 0;newBlock->next = best->next;best->size = size + sizeof(MemBlock_t);best->next = newBlock;}best->used = 1;return (void*)((uint8_t*)best + sizeof(MemBlock_t));
}// 内存释放
void Mem_Free(void *ptr) {if(!ptr) return;MemBlock_t *block = (MemBlock_t*)((uint8_t*)ptr - sizeof(MemBlock_t));block->used = 0;// 合并相邻空闲块MemBlock_t *curr = (MemBlock_t*)memPool;while(curr) {if(!curr->used && curr->next && !curr->next->used) {curr->size += curr->next->size;curr->next = curr->next->next;} else {curr = curr->next;}}
}

4.3 外部SRAM内存管理

// 使用FSMC外扩SRAM的内存管理
#define EXT_SRAM_BASE  0x60000000
#define EXT_SRAM_SIZE (512 * 1024) // 512KB// 初始化外部SRAM内存池
MemPool_t extSRAM_Pool;void ExtSRAM_Mem_Init(void) {// 先初始化FSMC (参考前文FSMC章节)MX_FSMC_Init();// 初始化内存池 (块大小=256字节)MemPool_Init(&extSRAM_Pool, (void*)EXT_SRAM_BASE, EXT_SRAM_SIZE, 256);
}// 从外部SRAM分配
void* ExtSRAM_Alloc(size_t size) {uint32_t blocks = (size + 255) / 256; // 计算所需块数uint8_t *base = NULL;// 寻找连续空闲块for(uint32_t i = 0; i < extSRAM_Pool.blockCount - blocks; i++) {uint32_t j;for(j = 0; j < blocks; j++) {if(extSRAM_Pool.memMap[i + j] != 0) break;}if(j == blocks) { // 找到连续空闲块base = (uint8_t*)extSRAM_Pool.memStart + i * 256;for(j = 0; j < blocks; j++) {extSRAM_Pool.memMap[i + j] = 1; // 标记为已用}return base;}}return NULL; // 分配失败
}// 释放外部SRAM内存
void ExtSRAM_Free(void *ptr, size_t size) {uint32_t offset = (uint32_t)ptr - (uint32_t)extSRAM_Pool.memStart;uint32_t startBlock = offset / 256;uint32_t blocks = (size + 255) / 256;for(uint32_t i = 0; i < blocks; i++) {if(startBlock + i < extSRAM_Pool.blockCount) {extSRAM_Pool.memMap[startBlock + i] = 0; // 标记为空闲}}
}
4.4 内存统计与监控
// 内存使用统计结构
typedef struct {uint32_t totalSize;      // 总内存大小uint32_t usedSize;       // 已使用内存uint32_t maxUsed;        // 历史最大使用量uint32_t allocCount;     // 当前分配块数uint32_t freeCount;      // 当前空闲块数uint32_t allocFailures;  // 分配失败次数
} MemStats_t;// 全局内存统计
MemStats_t memStats;// 更新内存统计 (在分配/释放时调用)
void Update_Mem_Stats(size_t size, int alloc) {if(alloc) {memStats.usedSize += size;memStats.allocCount++;if(memStats.usedSize > memStats.maxUsed) {memStats.maxUsed = memStats.usedSize;}} else {memStats.usedSize -= size;memStats.freeCount++;}
}// 内存溢出保护宏
#define SAFE_ALLOC(ptr, size) do { \ptr = malloc(size); \if(!ptr) { \memStats.allocFailures++; \ERROR_HANDLER(); \} else { \Update_Mem_Stats(size, 1); \} \
} while(0)#define SAFE_FREE(ptr, size) do { \if(ptr) { \free(ptr); \Update_Mem_Stats(size, 0); \ptr = NULL; \} \
} while(0)

4.5 内存保护机制

4.5.1 内存边界保护
// 带保护头的内存块
typedef struct {uint32_t guardStart; // 起始保护字size_t size;         // 实际分配大小uint8_t data[];      // 用户数据
} GuardedBlock_t;// 分配带保护的内存
void* Guarded_Malloc(size_t size) {GuardedBlock_t *block = malloc(sizeof(GuardedBlock_t) + size + sizeof(uint32_t));if(!block) return NULL;block->guardStart = 0xDEADBEEF;block->size = size;uint32_t *guardEnd = (uint32_t*)((uint8_t*)block + sizeof(GuardedBlock_t) + size);*guardEnd = 0xCAFEBABE;return block->data;
}// 检查内存损坏
int Check_Mem_Corruption(void *ptr) {GuardedBlock_t *block = (GuardedBlock_t*)((uint8_t*)ptr - offsetof(GuardedBlock_t, data));uint32_t *guardEnd = (uint32_t*)((uint8_t*)ptr + block->size);if(block->guardStart != 0xDEADBEEF || *guardEnd != 0xCAFEBABE) {return 1; // 内存损坏}return 0; // 内存正常
}// 释放带保护的内存
void Guarded_Free(void *ptr) {if(Check_Mem_Corruption(ptr)) {ERROR_HANDLER(); // 内存损坏处理}GuardedBlock_t *block = (GuardedBlock_t*)((uint8_t*)ptr - offsetof(GuardedBlock_t, data));free(block);
}
4.5.2 内存泄漏检测
// 内存分配记录
typedef struct {void *ptr;size_t size;const char *file;int line;
} AllocRecord;#define MAX_RECORDS 100
static AllocRecord allocRecords[MAX_RECORDS];
static int recordCount = 0;// 带调试信息的分配
void* Debug_Malloc(size_t size, const char *file, int line) {void *ptr = malloc(size);if(ptr && recordCount < MAX_RECORDS) {allocRecords[recordCount].ptr = ptr;allocRecords[recordCount].size = size;allocRecords[recordCount].file = file;allocRecords[recordCount].line = line;recordCount++;}return ptr;
}// 带调试信息的释放
void Debug_Free(void *ptr, const char *file, int line) {for(int i = 0; i < recordCount; i++) {if(allocRecords[i].ptr == ptr) {// 移除记录for(int j = i; j < recordCount - 1; j++) {allocRecords[j] = allocRecords[j+1];}recordCount--;free(ptr);return;}}// 未找到记录 (双重释放或无效指针)ERROR_HANDLER();
}// 检测内存泄漏
void Check_Mem_Leaks(void) {if(recordCount > 0) {printf("Memory leaks detected: %d\n", recordCount);for(int i = 0; i < recordCount; i++) {printf("Leak %d: %d bytes at %p (%s:%d)\n", i+1, allocRecords[i].size, allocRecords[i].ptr, allocRecords[i].file, allocRecords[i].line);}}
}// 使用宏简化调用
#define MALLOC(size) Debug_Malloc(size, __FILE__, __LINE__)
#define FREE(ptr) Debug_Free(ptr, __FILE__, __LINE__)

4.6 多内存池管理

// 内存池类型
typedef enum {MEM_POOL_FAST,      // 内部SRAM (快速)MEM_POOL_LARGE,      // 外部SRAM (大容量)MEM_POOL_PERMANENT   // 永久存储 (Flash模拟)
} MemPoolType;// 多池内存管理
void* Pooled_Malloc(size_t size, MemPoolType type) {switch(type) {case MEM_POOL_FAST:return InternalSRAM_Alloc(size);case MEM_POOL_LARGE:return ExtSRAM_Alloc(size);case MEM_POOL_PERMANENT:return FlashStorage_Alloc(size);default:return NULL;}
}void Pooled_Free(void *ptr, MemPoolType type) {switch(type) {case MEM_POOL_FAST:InternalSRAM_Free(ptr);break;case MEM_POOL_LARGE:ExtSRAM_Free(ptr);break;case MEM_POOL_PERMANENT:// 永久存储通常不支持释放break;}
}

文中完整工程下载:https://github.com/hazy1k/STM32F4-Quick-Start-Guide-HAL/tree/main/2.code

http://www.lryc.cn/news/627015.html

相关文章:

  • 建模工具Sparx EA的多视图协作教程
  • PyTorch - Developer Notes
  • 吴恩达 Machine Learning(Class 3)
  • 国产化PDF处理控件Spire.PDF教程:如何使用 Python 添加水印到 PDF
  • Linux命令大全-ps命令
  • Linux系统之部署nullboard任务管理工具
  • 基于springboot中学信息技术课程教学网站
  • 栈上创建和堆上创建区别
  • Nginx 的完整配置文件结构、配置语法以及模块详解
  • 设计模式1-单例模式
  • 继续记事本项目
  • 盲盒商城h5源码搭建可二开幸运盲盒回收转增定制开发教程
  • Hyperledger Fabric官方中文教程-改进笔记(十三)-使用测试网络创建通道
  • Google Chrome 扩展不受信任 - 不受支持的清单版本 解决方案
  • 整体设计 之定稿 “凝聚式中心点”原型 --整除:智能合约和DBMS的在表层挂接 能/所 依据的深层套接
  • AR 虚实叠加技术在工业设备运维中的实现流程方案
  • 云原生环境下的ITSM新趋势:从传统运维到智能化服务管理
  • MySQL 50 道经典练习题及答案
  • YOLOv8n-pose 模型使用
  • 学习中需不需要划线、做笔记
  • LLM常见名词记录
  • 【0基础PS】图片格式
  • 奇怪的“bug”--数据库的“隐式转换”行为
  • 深入解析:生产环境 SQL 数据库的架构设计与工程实践
  • 【clion】visual studio的sln转cmakelist并使用clion构建32位
  • 深入理解MySQL Ⅳ -- SQL性能分析工具
  • 欧州服务器String 转 double 有BUG?
  • skyworking 8.9.X 安装配置
  • 云原生俱乐部-RH294知识点归纳(1)
  • 【ElasticSearch实用篇-03】QueryDsl高阶用法以及缓存机制