memheap死机问题的分析与解决

发布时间:2024-01-11  

验证环境

NUCLEO-L476RG 开发板,板载 STM32L476RGT6(96K SARM1 + 32K SRAM2)


Win10 64 位

Keil MDK 5.36

RT-Thread 5.0.1 版本(2023-05-28 master 主线)

bsp : bspstm32stm32l476-st-nucleo

功能描述

最近在研究 RT-Thread 内存的管理,熟悉了一下 memheap 的功能实现,并且了解到 memheap 支持多块内存(物理地址不连续)的管理,当开启 memheap 后,rt_malloc 可以遍历所有注册过的 memheap 内存块,并且进行 内存的申请与释放。

当前 STM32L476RGT6 支持两块 SRAM,其中 SRAM1 96KB,还有一块 SRAM2 32KB,SRAM2 默认没有使用,尝试开启 SRAM2

环境搭建

stm32l476-st-nucleo 开启 memheap 的方法

1.jpg

stm32l476-st-nucleo 开启 SRAM2 的方法

#define HEAP_SRAM2_BEGIN (0x10000000)

#define HEAP_SRAM2_SIZE (32 * 1024)

static struct rt_memheap memheap_sram2;

int system_sram2_init(void)

{

return rt_memheap_init(&memheap_sram2, "sram2", (void *)HEAP_SRAM2_BEGIN, (rt_size_t)HEAP_SRAM2_SIZE);

}

INIT_BOARD_EXPORT(system_sram2_init);

功能测试

写两个测试命令:一直申请内存直到无法申请内存,一直释放所以申请的内存,确认 rt_malloc 会自动到新增加的 memheap SRAM2 中申请内存

功能验证通过,但是遇到死机问题

测试的函数

void *user_alloc(rt_size_t size)

{

return rt_memheap_alloc(&memheap_sram2, size);

}

void user_free(void *ptr)

{

rt_memheap_free(ptr);

}

void user_alloc_test(void)

{

for (int i = 0; i < MEMHEAP_BLOCK_NUM; i++)

{

user_ptr[i] = user_alloc(500);

if (!user_ptr[i])

{

rt_kprintf("malloc failed, index = %d ", i);

return;

}

else

{

rt_kprintf("[%d] : 0x%08x ", i, user_ptr[i]);

}

}

}

MSH_CMD_EXPORT(user_alloc_test, user_alloc_test);

void user_free_test(void)

{

for (int i = 0; i < MEMHEAP_BLOCK_NUM; i++)

{

if (user_ptr[i])

{

rt_kprintf("[%d] : 0x%08x ", i, user_ptr[i]);

user_free(user_ptr[i]);

}

}

}

MSH_CMD_EXPORT(user_free_test, user_free_test);

死机的信息

1.jpg

死机后,打印线程,发现 idle 线程栈异常

1.jpg

开启 CmBacktrace 组件后,发现死机的问题不是固定的,申请申请一个小内存,都会触发异常

问题分析

idle 线程的结构数据被破坏了,这就说明,内存越界了,但是测试例程只调用了 RT-Thread memheap 的 内存申请与释放 API,并没有其他的操作

手动申请一块内存,没有触发死机, list thread 发现,idle 线程的栈数据,依旧是异常的!

1.jpg

由于 开发板可以 单步调试,所以经过单步调试,加上分析,确认内存的范围,各个线程栈的内存范围,发现了一个奇怪的问题: 申请的内存偶尔会与线程栈的【静态内存】重叠

由于死机问题并不是必现,但是 idle 线程栈数据异常是必现的。当前怀疑 memheap 的内存范围设置存在问题,通过对比其他开发板的 bsp,发现了问题所在。

原来 bsp stm32l476-st-nucleo 系统的内存 HEAP_BEGIN 设置有问题,直接设置的 第一块内存的起始地址:

#define STM32_SRAM1_START (0x20000000)

#define HEAP_BEGIN STM32_SRAM1_START

初步看上去好像没有问题,其实RT-Thread 开机后,静态的内存数据、线程栈,依旧会占用一些内存,也就是其实内存地址,不能设置为 STM32_SRAM1_START,而是 【剩余内存】

【剩余内存】或者叫【空闲内存】的获取方法如下:

#if defined(__ARMCC_VERSION)

extern int Image$$

RW_IRAM1

ZI

Limit;

#define HEAP_BEGIN ((void *)&Image

RW_IRAM1

ZI

Limit)
#elif ICCARM
#pragma section="CSTACK"
#define HEAP_BEGIN (__segment_end("CSTACK"))
#else
extern int __bss_end;
#define HEAP_BEGIN ((void *)&__bss_end)
#endif
如在 Keil MDK5 上,是 #define HEAP_BEGIN ((void *)&Image

RW_IRAM1

ZI$$Limit), 也就是 SRAM1 的 剩余内存作为系统 堆内存使用,而不是 SRAM1 的全部内存作为 堆内存使用

解决方法

如上,重新设置 HEAP_BEGIN 即可

编译发现 RW_IRAM1 不存在,需要修改链接文件:bspstm32stm32l476-st-nucleooardlinker_scriptslink.sct,增加 RW_IRAM1 的定义

1.jpg

RW_IRAM1 0x20000000 0x00018000 { ; RW data

.ANY (+RW +ZI)

}

以上修改后,memheap 内存测试通过,不再触发死机

小结

memheap 使用起来还是比较的简单,可以通过设置 开启 RT_USING_MEMHEAP_AUTO_BINDING,也就是 勾选 [*] Use all of memheap objects as heap,决定新增加的 memheap 的内存是否参与系统常规的内存管理,如 rt_malloc、rt_free

用户可以单独的实现自己的 memheap 内存块 alloc、free 函数,这样只操作特定的 memheap。

当前的一个小缺点:如果 memheap 内存块较多,超过2个,如 RAM1、RAM2、RAM3,并且开启了 [*] Use all of memheap objects as heap,想实现 RAM1与 RAM2 作为系统通用内存管理,RAM3 用户专用内存管理,那么当前的 memheap 机制做不到,因为 rt_malloc 依旧会在 RAM1、RAM2 不能申请内存时,去 RAM3 申请内存。


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>