STM32 堆栈溢出问题

STM32 堆栈溢出问题

当我们创建稍微复杂一点的系统时,堆栈溢出问题极易出现!

1、一般RAM最后两块空间是堆heap、栈stack,堆从下往上用,栈从上往下用,任意 一个用完都会进入对方空间。

2、如果栈用完,进入堆空间,这个时候系统是不会有任何异常的,也就是说,栈底是没有什么意义的。除非堆和栈指针重叠,否则一切正常,尽管栈指针指向了堆空间。

3、如果栈用完,进入堆的空间,这个时候系统是不会有异常的,但是堆栈会相互修改数据。最难受的就是,栈里面保存的指针地址,一旦被堆空间修改了栈空间中的指针数据,当再次调用占空间中的地址指针时,会跳到无效的内存空间中去,然而这个时候,系统仍然不会报错,但系统运行和数据已经错乱了。

4、如果使用KEIL微库,堆空间用完,再malloc的时候,会得到空指针,但是不会报错。然而如果使用C++的new,这个时候会报错。

以下代码是内存堆栈溢出检测模块。

#ifdef DEBUG

void* operator new(uint size);
void* operator new[](uint size);
void operator delete(void * p);
void operator delete [] (void * p);

#endif
extern uint __heap_base;
extern uint __heap_limit;

void* operator new(uint size)
{
    debug_printf(" new size: %d ", size);
    void * p = malloc(size);
    if(!p)
        debug_printf("malloc failed! size=%d ", size);
    else
    {
        debug_printf("0x%08x ", p);
        // 如果堆只剩下64字节,则报告失败,要求用户扩大堆空间以免不测
        uint end = (uint)&__heap_limit;
        if((uint)p + size + 0x40 >= end) debug_printf(" + %d near HeapEnd=0x%08x", size, end);
    }
    assert_param(p);
    return p;
}

void* operator new[](uint size)
{
    debug_printf(" new size[]: %d ", size);
    void * p = malloc(size);
    if(!p)
        debug_printf("malloc failed! size=%d ", size);
    else
    {
        debug_printf("0x%08x ", p);
        // 如果堆只剩下64字节,则报告失败,要求用户扩大堆空间以免不测
        uint end = (uint)&__heap_limit;
        if((uint)p + size + 0x40 >= end) debug_printf(" + %d near HeapEnd=0x%08x", size, end);
    }
    assert_param(p);
    return p;
}

void operator delete(void * p)
{
    debug_printf(" delete 0x%08x ", p);
    if(p) free(p);
}

void operator delete[](void * p)
{
    debug_printf(" delete[] 0x%08x ", p);
    if(p) free(p);
}```

## 5、我们在实际使用过程中,可能不会像上面一样再去封装检测函数,那这个时候需要我们自己去多多注意相关的问题。
①比如函数调用纵深不宜过深,五个左右为最高。其次,局部变量不要太多太大,尤其是局部数组,大容量的数组定义为全局变量,减少栈空间的占用。
②堆的问题。如果堆空间较小,而需要中间缓存malloc的空间很大,这个时候,很容易导致堆指针指向栈空间,修改栈内数据,导致运行错误。所以,我们要评估我们的malloc缓存空间,在free之前,同时可能存在的最大内存为多少,然后修改启动文件中对应的size,减少系统出错的问题。
### STM32 发生堆栈溢出时的现象、原因及解决方案 #### 现象 当STM32发生堆栈溢出时,可能会触发HardFault异常。这种情况下,程序的行为变得不可预测,可能导致系统崩溃或进入无限循环。通常,在调试环境中会观察到程序突然停止运行或者重启[^1]。 #### 原因 堆栈溢出通常是由于函数调用链过深或是局部变量分配过多造成的。如果应用程序中存在递归调用而未设置合理的终止条件,则容易引发此类错误。另外,不当的大规模数据结构作为自动变量声明也会消耗大量栈空间,从而导致溢出。此外,中断服务例程(ISR)内部操作复杂度高同样能引起此问题,因为ISR共享主线程的栈资源。 #### 解决方案 为了防止和处理堆栈溢出: - **增加堆栈大小**:可以通过调整启动文件中的定义来增大任务堆栈尺寸;对于操作系统环境下的多线程应用来说,还可以单独为每个线程指定更大的初始堆栈容量。 - **优化算法设计**:减少不必要的递归调用次数,避免创建过大体积的本地数组或其他静态存储类对象。 - **监控剩余可用堆栈量**:利用一些工具如IAR Embedded Workbench提供的诊断特性,或者是编写自定义代码定期检查当前使用的最大堆栈深度,并据此动态调整策略以预防潜在风险。 - **合理规划全局/静态变量布局**:确保这些变量不会占用过多内存区域以至于压缩了留给活动记录的空间。 - **使用专用库管理动态内存分配**:例如采用`malloc()`替代直接在栈上开辟临时缓冲区的方式完成相似需求的任务。 ```c // 示例:通过修改链接器脚本改变默认堆栈大小 MEMORY { /* ... */ } SECTIONS { .stack : { _estack = ORIGIN(RAM) + LENGTH(RAM); . = ALIGN(8); *(.noinit) *(.bss) *(COMMON) . = _estack - 0x400; // 设置新的堆栈大小为1KB (此处仅为示例值,请根据实际需要设定) } >RAM AT> FLASH } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值