我试图了解当RAM满时 malloc()
如何在普通的 C
上行为 .
我的STM32有20kB = 0x5000Bytes的RAM,0x200用于堆栈 .
#include <stdlib.h>
#include "stm32f10x.h"
struct list_el {
char weight[1024];
};
typedef struct list_el item;
int main(void)
{
item * curr;
// allocate until RAM is full
do {
curr = (item *)malloc(sizeof(item));
} while (curr != NULL);
// I know, free() is missing. Program is supposed to crash
return 0;
}
一旦堆太小而无法分配,我希望 malloc()
返回 NULL
:
0x5000
(RAM) - 0x83C
(bss) - 0x200
(stack)= 0x45C4
(堆)
所以当第18次执行 malloc()
时 . 一项是1024 = 0x400
字节大 .
但是相反,uC在第18次之后调用 HardFault_Handler(void)
(甚至不是 MemManager_Handler(void)
)
有没有人建议如何预测 malloc()
失败 - 因为等待 NULL
返回似乎不起作用 .
谢谢 .
3 回答
它看起来不像
malloc
正在进行任何检查 . 您得到的故障来自硬件检测到写入无效地址,这可能来自malloc
本身 .当
malloc
分配内存时,它从内部池中获取一个块,并将其返回给您 . 但是,它需要为free
函数存储一些信息才能完成释放 . 通常,这是块的实际长度 . 为了保存该信息,malloc
从块本身的开头起占用几个字节,在那里写入信息,并将地址返回到已写入自己信息的地点 .例如,假设您要求一个10字节的块 .
malloc
将获取一个可用的16字节块,比如地址0x3200..0x320F
,将长度(即16)写入字节1和2,然后将0x3202
返回给您 . 现在你的程序可以使用从0x3202
到0x320B
的十个字节 . 其他四个字节也可用 - 如果您调用realloc
并要求14个字节,则不会重新分配 .当
malloc
将长度写入将要返回给您的内存块时,关键点就出现了:它写入的地址需要有效 . 似乎在第18次迭代之后,下一个块的地址是负的(转换为非常大的正值),因此CPU捕获写入,并触发硬故障 .在堆和堆栈相互增长的情况下,没有可靠的方法来检测内存不足,同时让你使用内存的每个最后一个字节,这通常是一件非常理想的事情 .
malloc
无法预测分配后您将使用多少堆栈,因此它甚至都没有尝试 . 这就是为什么在大多数情况下字节计数在你身上 .通常,在嵌入式硬件上,当空间限制为几十千字节时,您可以避免在"arbitrary"位置进行
malloc
调用 . 相反,您使用一些预先计算的限制预先分配所有内存,并将其分配给需要它的结构,并且永远不再调用malloc
.您的程序很可能因为 illegal memory access 而崩溃,这几乎总是 legal memory access 的间接(后续)结果,但您不打算执行 .
例如(这也是我对你系统上发生的事情的猜测):
你的堆最有可能在堆栈之后开始 . 现在,假设您在
main
中有堆栈溢出 . 然后,您在main
中执行的其中一个操作(就您而言自然是合法操作)将使用一些"junk"数据覆盖堆的开头 .作为后续结果,下次尝试从堆中分配内存时,指向下一个可用内存块的指针不再有效,最终导致内存访问冲突 .
首先,我强烈建议您将堆栈大小从0x200字节增加到0x400字节 . 这通常在链接器命令文件中定义,或通过IDE在项目的链接器设置中定义 .
如果您的项目是在IAR上,那么您可以在
icf
文件中更改它:除此之外,我建议您在
HardFault_Handler
中添加代码,以便在崩溃之前重建调用堆栈并注册值 . 这可能允许您跟踪运行时错误并找出确切的位置 .在文件'startup_stm32f03xx.s'中,确保您有以下代码:
然后,在同一个文件中,添加以下中断处理程序(所有其他处理程序所在的位置):
然后,在文件'stm32f03xx.c'中,添加以下ISR:
如果你不能在执行时使用
printf
发生此特定的硬故障中断,然后将所有上述数据保存在全局缓冲区中,以便在到达while (1)
后查看 .然后,请参阅http://www.keil.com/appnotes/files/apnt209.pdf处的'Cortex-M Fault Exceptions and Registers'部分以了解问题,或者如果您需要进一步的帮助,请在此处发布输出 .
UPDATE:
除上述所有内容外,请确保正确定义堆的基址 . 它可能在项目设置中进行硬编码(通常在数据部分和堆栈之后) . 但它也可以在运行时,程序的初始化阶段确定 . 通常,您需要检查数据部分的基地址和程序堆栈(在构建项目后创建的映射文件中),并确保堆中的任何一个都不重叠 .
我曾经有过这样一种情况,即堆的基地址被设置为一个常量地址,这一点很好 . 但随后我通过向程序添加全局变量逐渐增加了数据部分的大小 . 堆栈位于数据部分的正后方,随着数据部分变大,它“向前移动”,因此它们中的任何一个都没有问题 . 但最终,堆被“分配”在堆栈的“顶部” . 因此,在某些时候,堆操作开始覆盖堆栈上的变量,堆栈操作开始覆盖堆的内容 .
使用标准
c malloc
很难区分,而且我认为malloc
似乎有些错误 . 因此,您可以使用RAM地址实现一些自定义malloc
来管理内存 .我不确定这可能对你有所帮助,但我在我的控制器相关项目中做了一些自定义
malloc
,如下所示这个基本上宏定义了RAM地址,并且手动为块大小手动选择了更多的块号,这通常需要分配,像36个字节需要我更多,所以我需要更多的数字 .
这是mem init的init函数
这一个用于分配
这个是免费的
毕竟你可以使用上面的功能
然后还可以按如下方式观察您使用的内存
通常先预先计算内存然后按照我的要求给出 .