内存管理¶
本文说明 Solution 内存管理的关键接口、配置入口和常见使用场景。
1. 参考代码¶
文件 |
作用 |
|---|---|
|
提供应用内存申请、释放和统计接口 |
|
管理大块内存申请和释放 |
|
基于 TLSF 管理应用动态内存池 |
|
给项目定制内存区域和分配策略 |
2. 芯片内存配置基础¶
内存配置需先明确芯片硬件特性,核心关注 核结构、内存类型 及 存储支持,具体如下:
2.1 核结构与内存分配¶
核类型: 芯片分为 单核(如 52x 系列)和 双核(含低功耗小核 LCPU + 高性能大核 HCPU )。
双核场景:需根据功能对性能/功耗的需求分配核资源(如低功耗任务放在 LCPU,高性能任务放在 HCPU )。
单核场景(52x 系列):硬件为单核,但软件架构虚拟“LCPU”(实际运行在 HCPU 上),确保跨平台代码兼容性。
内存类型: 包括 SRAM(高速、小容量)和 PSRAM(低速、大容量)。
LCPU 通常仅挂载 SRAM;部分 SF32LB5x 系列 LCPU 可能挂载 NOR Flash(需根据功能需求选择芯片型号 )。
2.2 存储方案差异¶
SF32LB5x 系列支持两种存储方案,内存配置需适配:
2.2.1 NOR Flash 方案¶
特性:支持 XIP(直接执行),内存需求较低。
说明:代码和资源可直接从 NOR Flash 运行,无需额外搬运。
2.2.2 NAND Flash/eMMC 方案¶
特性:不支持 XIP,需注意以下两点:
PSRAM 容量需 ≥8MB(用于加载代码和资源 );
代码(
code.bin)和图片资源需从 NAND Flash/eMMC 搬运到 PSRAM 后才能执行/访问。
3. 内存分配与配置¶
内存主要用于 9 类场景,其存储位置(SRAM/PSRAM)通过链接脚本(如 solution\framework\__template__\config\hcpu\linker_script\link_flash_52x.sct)配置,具体如下:
No |
内存用途 |
说明与配置方式 |
|---|---|---|
1 |
Code/RO |
必须常驻内存的代码及只读数据,由链接脚本指定存储位置 |
2 |
RW/ZI |
可读写数据(RW)和初始化为 0 的数据(ZI),默认随代码分配 |
3 |
帧缓冲区(framebuffer) |
用于 GUI 显示,配置路径: |
4 |
堆内存(Heap) |
包括系统堆(system heap)和专用堆(memheap),配置路径: |
5 |
场景动画切换 buffer |
支持应用切换动画时所需缓冲区,配置路径: |
6 |
线程栈(Thread stack) |
建议静态分配,频繁使用栈放SRAM(减少Cache换入换出提升性能) |
7 |
图形加速资源 |
图片旋转缓存从 SRAM 堆分配,绘图 canvas 从 PSRAM 堆分配 |
8 |
NAND Flash/eMMC 的 code.bin |
因不支持 XIP,开机时从 NAND Flash/eMMC 搬运到 PSRAM 执行 |
9 |
NAND Flash/eMMC 的图片资源 |
需先搬运到 PSRAM 才能被 ePIC 硬件访问,需使用 PSRAM 堆 |
4. 堆内存(Heap)详解¶
Solution 在 app_mem.c 中提供多种堆内存管理接口,包括块内存、系统堆及专用堆,特性如下:
4.1 块内存(可选)¶
配置:通过
menuconfig → (Top) → Memory Config → Using block memory开启。特性:
优点:分配速度快,无内存碎片;
缺点:需预先定义块大小和数量,复用性低,易浪费内存。
适用场景:映射
lv_mem(LVGL 琐碎内存分配 ),通过app_malloc申请(申请失败时自动转用系统堆 ),建议应用小内存优先使用。
4.2 系统堆(system heap)¶
分配接口:
rt_malloc。存储位置:可配置在 SRAM 或 PSRAM(通过
menuconfig → (Top) → Memory Config → Sysheap config in PSRAM指定)。管理方式:
RT_USING_MEMHEAP_AS_HEAP未被定义: 小内存管理,此种模式下内存利用率高,但分配速度慢(适合 SRAM,因容量有限 );RT_USING_MEMHEAP_AS_HEAP被定义: 大内存管理,此种模式下分配速度快,但易产生碎片(适合 PSRAM,因容量较大 )。
配置系统堆Size:
SYS_HEAP_IN_PSRAM未被定义: 自动计算,系统堆Size = SRAM总Size - 已经分配的SRAM大小(静态变量,包括栈)SYS_HEAP_IN_PSRAM被定义:通过menuconfig → (Top) → Memory Config → Sys heap size in PSRAM配置。
4.3 SRAM memheap / PSRAM memheap¶
分配接口:
app_cache_alloc(需指定CACHE_SRAM或CACHE_PSRAM);特性:采用大内存管理(分配快,易碎片 );
适用场景:
SRAM 堆用于高速度需求的内存(受容量限制 );
PSRAM 堆用于消息存储、图片缓存、canvas 等。
注:PSRAM 堆大小自动计算,无需手动配置。
配置:
SRAM HEAP:通过
menuconfig → (Top) → Memory Config → Cache in sram size(bytes)配置。PSRAM HEAP:在
app_mem.c中通过#define PSRAM_HEAP_SIZE (PSRAM_BASE + PSRAM_SIZE - PSRAMHEAP_BASE)自动计算。计算原理:PSRAM总的大小 - 已经分配的PSRAM大小(静态变量和动态的专用heap)
4.4 Freetype memheap¶
分配接口:
ft_malloc用途:专用于字体渲染(支持 Freetype 时需配置 )。
容量建议:
NAND Flash/eMMC 方案:建议 ≥500KB;
NOR Flash 方案:建议 ≥200KB。
配置:通过
menuconfig → (Top) → Memory Config → FREETYPE_CACHE size(bytes)指定堆大小。
4.5 TLSF 内存管理(可选)¶
TLSF(Two-Level Segregated Fit,双级隔离适配)是一种面向实时系统的动态内存分配算法。它按块大小建立一级、二级空闲链表,并用位图记录可用链表;分配时通过位图快速定位满足请求的空闲块,释放时与相邻空闲块合并。因此,分配和释放的执行时间更稳定,适合 UI、图片缓存、协议栈等频繁申请和释放不同大小内存的场景。
覆盖范围:启用后,TLSF 替换系统堆及所有通过
rt_memheap_init初始化的memheap的底层分配实现,包括 SRAM、PSRAM 和 Freetypememheap。rt_malloc先尝试系统堆,再尝试已加入系统堆链表的memheap;当前app_mem.c会将 PSRAMmemheap加入该链表,因此 PSRAM 可作为系统堆的后备空间。不覆盖的机制:块内存由
bmem_alloc独立管理,TLSF 不改变其块大小、块数量和分配规则。app_malloc仍优先申请块内存;块内存分配失败后,才调用 TLSF 接管的rt_malloc。适用场景:长时间运行且存在大量不同尺寸动态内存申请/释放,缺省内存管理出现碎片、分配耗时波动或大块内存申请失败时,可评估启用。
注意事项:TLSF 不能修复内存泄漏、越界或释放后使用等问题;它会在每个堆起始处存放控制信息,并保留每个分配块的管理头,因此小块频繁申请时仍有元数据开销。启用前应确认各堆容量余量,启用后需回归验证高频分配、页面切换和长时间运行场景。
与其他配置的关系:TLSF 与
USING_BLOCK_MEM可同时使用,二者并不互斥;启用 TLSF 后,MSG_CACHE_SIZE配置项不可用,由 TLSF 统一管理对应动态分配需求。
menuconfig 配置步骤:
执行
menuconfig,进入menuconfig → (Top) → Memory Config。将
Use TLSF as memory managment设为y,生成宏APP_USING_TLSF_MEM。根据产品内存方案继续配置
Sysheap config in PSRAM、Sys heap size in PSRAM、PSRAM_CACHE size(bytes)和Cache in sram size(bytes);TLSF 不替代这些堆容量与存储位置配置。重新编译并烧录。启用后可在 FinSH 中执行
list_memheap查看各堆总量、已用量和峰值;执行list_tlsf_heap查看 TLSF 块布局和空闲块状态。
5. 为某个工程单独定义块内存大小¶
块内存默认定义在app_mem.c中,
#ifndef APP_BMEM_SELF_DEFINED
#if defined (USING_BLOCK_MEM) && defined (BSP_USING_PSRAM)
#if defined (SOLUTION_RES_USING_NAND)
#define _X 200
#else
#define _X 0
#endif
PSRAM_RET_SECT_BEG;
ALIGN_4
BMEM_REGISTER(16, (400 + _X));
BMEM_REGISTER(32, (200 + _X));
BMEM_REGISTER(48, (200 + _X));
BMEM_REGISTER(64, (200 + _X));
BMEM_REGISTER(100, (200 + _X));
PSRAM_RET_SECT_END
#endif
#endif
app_mem.c属于框架类c程序,如果客户定义了多个产品,一些产品对块内存的配置不一样,则可以通过如下的步骤为某个工程单独配置块内存:
通过
menuconfig → (Top) → Memory Config → User-defined block memory size定义APP_BMEM_SELF_DEFINED。copy
__template__\project\__applicaiton_private__目录到在project/hcpu下面,修改app_mem_custom.c中BMEM_REGISTER中的参数block大小以及个数。
6. 内存头信息¶
内存的管理,app_mem.c在RTT的内核函数的基础上做了一层封装,内存申请/释放主要依赖两个RTT的文件mem.c和memheap.c。
6.1 rt_xxx (malloc/free/calloc/realloc)¶
依赖于是否打开宏RT_USING_MEMHEAP_AS_HEAP,
如果
RT_USING_MEMHEAP_AS_HEAP定义,则rt_malloc/rt_free/rt_calloc/rt_realloc定义在memheap.c,此时内存头为struct rt_memheap_item类型
struct rt_memheap_item
{
rt_uint32_t magic; /**< magic number for memheap */
struct rt_memheap *pool_ptr; /**< point of pool */
rt_uint32_t size; /**< requested memory size excluding header*/
#ifdef RT_USING_MEMTRACE
//rt_uint8_t thread[4]; /* thread name */
rt_uint32_t ret_addr;
rt_tick_t tick;
//rt_uint32_t dbg_magic;
#endif
#ifdef MEM_ASYN_FREE
rt_uint16_t ref_count_magic;
rt_uint16_t ref_count;
#endif
struct rt_memheap_item *next; /**< next memheap item */
struct rt_memheap_item *prev; /**< prev memheap item */
struct rt_memheap_item *next_free; /**< next free memheap item */
struct rt_memheap_item *prev_free; /**< prev free memheap item */
};
Solution默认是打开RT_USING_MEMTRACE,板子上默认是打开MEM_ASYN_FREE的。因此,此时头信息总共有40个字节(未打开MEM_ASYN_FREE则为36个字节)。
如果未被定义,则
rt_malloc/rt_free/rt_calloc/rt_realloc定义在mem.c中,此时内存头为struct heap_mem *类型
struct heap_mem
{
/* magic and used flag */
rt_uint16_t magic;
rt_uint16_t used;
struct heap_mem *next, *prev;
rt_uint32_t size; /**< requested memory size excluding header*/
#ifdef RT_USING_MEMTRACE
#ifdef RT_MEM_RECORD_THREAD_NAME
rt_uint8_t thread[4]; /**< thread name */
#endif /* RT_MEM_RECORD_THREAD_NAME */
rt_uint32_t ret_addr;
rt_tick_t tick;
#endif
#ifdef MEM_ASYN_FREE
rt_uint16_t ref_count_magic;
rt_uint16_t ref_count;
#endif
};
Solution默认是打开RT_USING_MEMTRACE(但RT_MEM_RECORD_THREAD_NAME未打开),板子上默认是打开MEM_ASYN_FREE的。因此,此时头信息总共有28个字节(未打开MEM_ASYN_FREE则为24个字节)。
6.2 rt_memheap_xxx (alloc/free/calloc/realloc)¶
定义在memheap.c,此时内存头为 struct rt_memheap_item类型。
6.3 app_xxx (malloc/free/calloc/realloc)中申请的块内存¶
定义在app_bmem.h,此时内存头为struct struct_bmem_t或 bmem_item_t类型。
7. 内存信息查看¶
在恢复现场后,在trace32中装入:
sdk\tools\crash_dump_analyser\script\show_heap.cmm: 显示通过mem.c中函数申请的内存sdk\tools\crash_dump_analyser\script\show_rt_memheap.cmm:显示通过memheap.c中函数申请的内存sdk\tools\crash_dump_analyser\script\show_bmem.cmm:显示已经申请但未释放的块内存
装入路径为:trace32 -> File -> Run Script -> sdk\tools\crash_dump_analyser\script\xxx.cmm
8. 内存异常导致的死机及原因¶
常见内存异常场景如下(需重点规避 ):
内存越界:分配后使用范围超出申请大小。
野指针操作:未初始化/未申请的指针直接使用,覆盖其他内存或访问非法地址。
非保持区内存误用:睡眠唤醒后未重新初始化 non-retention 区域内存,继续使用。
退栈异常:野指针导致函数返回时栈错误。
回调函数指针异常:指针未赋值或指向非法地址。
释放后使用:内存已
rt_free或·app_cache_free释放,仍继续访问。多线程互斥缺失:同一片内存被多线程并发访问,数据异常。
Cache 操作错误:未执行
mpu_dcache_clean或mpu_dcache_invalidate,导致数据不一致。