嵌入式系统内存使用情况评估方法探究
Daniel 2016-7-22
有一段时间没有写过博客了,这段时间抽空做做记录。总结和记录是对知识最好的回忆与升华。
这段时间遇到MCU程序总是莫名奇妙的跑飞,插上仿真器这样的现象又不会出现,多次怀疑与内存大小有关,但如何才能知道运行中的程序内存的占用情况呢?是不是堆栈使用太多超出了设定的范围?
所有的疑问最终都定位到malloc函数上,有时候运行malloc就会死机,但有时候又不会。后来又做了一个实验,把这个工程移植到了最新的编译器上就没问题了,还真是奇怪,是不是老编译器的bug导致?
为了一探究竟,做了以下实验:
1. 在老的编译器仿真看malloc的执行结果:
申请18个字节,malloc实际申请为20个字节
申请34个字节,malloc实际申请为36个字节
70个字节以下都是正常的。
在分配72个字节时候, malloc头发生错误,显示分配了0x3FE8个字节即16360个,上下指针也不对
原来在老编译器对malloc的实现是,Malloc的管理头为16个字节,第一个为实际分配的size大小
{
Uint32_t size;
Uint32_t *pNext;
Uint32_t flag;
Uint32_t *pPre;
}共16个字节
2. 使用新编译器的测试
在测试malloc函数时,发现在新老编译器上执行的结果不同:
新编译器malloc头结构为
{
Uint32_t size;
Uint32_t *pNext;
},共8个字节
申请18个字节,malloc实际申请了32个字节
申请34个字节,malloc实际申请了44个字节
申请72个字节,malloc实际申请了80个字节
通过对比问题就显而易见了,老编译器感觉确实有问题,使用新编译器问题也得到了解决。
那么系统到底使用了多少内存呢?
我们知道嵌入式系统一般在初始化的时候吧内存分为了“internal ram“、“head”、“stack”。内存排列情况如下图所示:
internal ram主要用于数据段、BSS段;heap中主要存储程序员手动分配的变量,主要是用malloc分配的;stack中存储了临时变量、返回地址、函数参数、中断环境等。
其实程序在编译完后,程序所占用的flash和internal ram的大小就确定了,可以用memory map中计算得出,而堆栈使用情况就需要计算得出了。
计算堆剩余空间大小
结合开始部分对malloc的分析,可以使用malloc分配内存头中的记录来计算使用的大小,当调用free的时候根据内存头中的记录计算释放的大小。
在系统分配释放内存的函数中添加统计代码:
void * system_alloc(void *allocator_data, uint32_t size)//size_t
{
p=malloc(size);
if(p)
{
malloc_size = (*(unsigned int*)((unsigned char*)p - 8)) + 8;
}
return p;
}
Void system_free(void *allocator_data, void *data)
{
if(data)
{
//根据指针的位置找到分配内存的大小
free_size = (*(unsigned int*)((unsigned char*)data - 8)) + 8;
}
free(data);
}
统计malloc_size以及free_size就可以了。
堆的剩余空间为
Heap_len - (total_malloc_size - total_free_size)
计算栈空间剩余大小:
栈空间,在周期中断中定义一个局部变量,分配在栈顶上
uint32_t Stack_sp = 0;
void PIT0_ISR(void)
{ uint8_t test = 0;
Stack_sp = (uint32_t *)&test;
PIT.CH[0].TFLG.B.TIF = 1;
}
栈的剩余空间大致为 stack_len-(stack_org - Stack_sp)
Internal RAM剩余空间大小(数据段、BSS段等)
internal_ram_len – (internal_ram_org - internal_ram_sp)
internal_ram_sp:在MAP文件中可以计算得到
剩余的总RAM = 剩余heap+剩余stack+剩余Internal RAM
剩余的总flash = flash_size – 使用的flash(MAP中可以计算得到)