我在一个运行在AVR微控制器(ATMega328P)上的C程序中遇到了问题。我认为这是由于堆栈碰撞引起的,但我想确认一下。
是否有任何方法可以可视化堆栈和堆对SRAM使用情况?
注意:该程序使用avr-gcc编译,并使用avr-libc。
更新:我遇到的实际问题是malloc实现失败(返回NULL
)。所有malloc
操作都发生在启动时,所有free
操作都发生在应用程序结束时(实际上从不结束,因为应用程序的主要部分位于无限循环中)。所以我确定碎片不是问题。
我在一个运行在AVR微控制器(ATMega328P)上的C程序中遇到了问题。我认为这是由于堆栈碰撞引起的,但我想确认一下。
是否有任何方法可以可视化堆栈和堆对SRAM使用情况?
注意:该程序使用avr-gcc编译,并使用avr-libc。
更新:我遇到的实际问题是malloc实现失败(返回NULL
)。所有malloc
操作都发生在启动时,所有free
操作都发生在应用程序结束时(实际上从不结束,因为应用程序的主要部分位于无限循环中)。所以我确定碎片不是问题。
avr-size
工具来检查RAM的静态使用情况,如下所述:avr-size -C -x Filename.elf
(avr-size文档: http://ccrma.stanford.edu/planetccrma/man/man1/avr-size.1.html )
以下是如何在IDE上设置的示例: 在Code::Blocks中,选择项目 -> 构建选项 -> 预/后构建步骤 -> 后构建步骤,并包括:
avr-size -C $(TARGET_OUTPUT_FILE)
或
avr-size -C --mcu=atmega328p $(TARGET_OUTPUT_FILE)
构建结束后的示例输出:
AVR Memory Usage
----------------
Device: atmega16
Program: 7376 bytes (45.0% Full)
(.text + .data + .bootloader)
Data: 81 bytes (7.9% Full)
(.data + .bss + .noinit)
EEPROM: 63 bytes (12.3% Full)
(.eeprom)
int freeRam () {
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
这里是使用该代码的草图:
void setup () {
Serial.begin(57600);
Serial.println("\n[memCheck]");
Serial.println(freeRam());
}
freeRam()函数返回堆的末尾和栈上最后一个分配内存之间存在多少字节,因此它实际上表示堆/栈在碰撞之前可以增长多少。
您可以在怀疑可能导致堆栈/堆碰撞的代码周围检查此函数的返回值。
freeRam()
函数在释放的内存仍被 malloc 的“空闲列表”所持有的情况下会失败。这只有在频繁动态分配和释放内存的情况下才可能成为实际问题——在 AVR 上可能是一个罕见的场景(例如,在我的情况下,我仅在程序初始化期间动态分配内存,因此 freeRam()
代码对我有效)。 - Matthew Murdoch你说malloc失败并返回NULL:
首先要看的明显原因是你的堆栈“满了” - 也就是说,你要求malloc分配的内存无法得到分配,因为它不可用。
需要注意两种情况:
a: 你有一个16K的堆栈,你已经使用了10K并尝试再分配10K。你的堆栈太小了。
b: 更常见的情况是,你有一个16K的堆栈,你一直在进行malloc/free/realloc调用,你的堆栈空间不到50%是“满”的状态:你调用了1K的malloc并且失败了-这是怎么回事?答案是-堆栈空闲空间被碎片化了-没有连续的1K空闲内存可以返回。当发生这种情况时,C堆栈管理器不能压缩堆栈,所以你通常处于糟糕的状态。有技术可以避免碎片化,但很难知道这是否真正是问题所在。你需要在malloc和free中添加日志记录装置,以便了解正在执行哪些动态内存操作。
编辑:
你说所有的malloc都发生在启动时,所以碎片化不是问题。
在这种情况下,应该很容易用静态内存替换动态分配。
旧代码示例:
char *buffer;
void init()
{
buffer = malloc(BUFFSIZE);
}
新代码:
char buffer[BUFFSIZE];
在你到处都这样做后,链接器应该会提醒你如果所有内容都无法适应可用的内存。不要忘记减小堆的大小 - 但要注意有些运行时IO系统函数可能仍然使用堆,因此您可能无法完全删除它。
freeRam()
方法无法捕捉到的高堆栈使用情况(特别是当涉及中断时)。 - Cameron Tacklind