嵌入式操作系统
在开发过程中,我们有时候可能会遇到数据错误的情况,而这个情况发生多数是由于堆栈溢出导致,这里我们将详细讲解复现堆栈溢出会导致的问题及提供相应的解决方法。
先说结论,以STM32F103RCT6为例,初始的栈空间是1KB,堆空间是512Byte。如果动态内存分配需求过多时,需要手动调节堆空间。在启动文件startup_stm32f103xe.s的开头就可以设置堆栈空间大小。同样,在STM32CubeMX中也可对堆栈大小进行修改,在Project -》 Settings选项中可以对Minimum Heap Size大小进行更改。扩大之后即可解决堆栈空间不足的问题。
今天在STM32F103RCT6上,使用 malloc() 为链表分配内存空间时,忽然遇到一次分配内存过多而死机的问题。查阅官方文档发现此型号的单片机FLASH 256KB,RAM 48KB。我链表的结构体定义如下:
typedef struct LNode{
uint8_t data;
struct LNode *next;
}LNode,*LinkList;1234
uint8_t类型在单片机中定义为unsigned char即1个字节,32位系统一个指针变量为4字节。由于“内存对齐”机制的存在,所以实际上一个节点分配的内存为8字节。并且通过输出语句printf(“%d”,sizeof(*Head));打印到串口助手显示的也是8,证明的分析的正确性。
经过测试发现,我最多能创建32个节点,因此我只使用了32*8=256 byte 的内存空间。与官方文档的48KB的内存空间相差太多。
查阅网上的博客发现,堆栈大小可以在stm32的启动文件startup_stm32f103xe.s里面设置,开头就有:
Stack_Size EQU 0x400
Heap_Size EQU 0x20012
0x00000400 等于1024字节所以等于1K
0x00000200 等于512字节所以等于512 Byte
由于malloc()分配的动态内存在堆区域,因此调大堆空间Heap_Size为0xC00,即3072字节大小。重新测试,发现可以接收到191个节点,这次使用了191*8=1528 byte大小的内存空间。由此判断,用户可以自由使用的堆空间,大约为堆总空间的一半。超过时系统就会死机。
注:在STM32CubeMX中也可对堆栈大小进行修改,在Project -》 Settings选项中可以对Minimum Heap Size大小进行更改。
1 。建立一个测试工程
定义主函数
定义堆栈地址大小,分别都定义为0x00000200;
编译后查看生成的.map文件,可以看到栈起始地址为0x20000d50,大小为512byte。
程序定义了全局变量大小为32位(对应4字节)的700个数据,所以全局变量占用RAM空间计算:
G=0x20000000 + 4*700 = 0x20000AF0
由于栈地址是向下生长方式,我们定义了局部变量数据大小为500个32位的数,所以实际运行时局部变量占用RAM的起始地址为:
L=0x20000D50 + 512 – 4*500 = 0x20000780
所以局部变量占用RAM的地址范围为:0x20000780 --- 0x20000F50
由上计算可知,局部变量部分地址空间与全局变量使用的地址空间有冲突,这将导致程序运行过程全局变量数值被局部变量替换掉。
程序运行测试如下图所示:
全局变量初始化完成
初始化局部变量,由上图可看出原来全局变量的数值已被局部变量替换
4.1 理论计算
由之前可以算出局部变量使用了2000byte的RAM地址空间,由于堆栈地址设置要求是0x0100的整数倍,所以这里可以将大小设置为0x0800,即2048个字节数据。
4.2 设置栈空间大小
改大栈空间大小,如下图所示:
修改栈空间大小后查看生成的.map文件,如下图所示:
4.3 修改后测试验证
由上图测试可以知道,局部变量存储地址已经从0x20000D80开始,与全局变量存储地址不冲突,至此可以解决遇到的堆栈溢出问题。
上述方法已解决了我们遇到的堆栈溢出问题,但实际编程时,我们不建议在子函数中使用大数组
全部0条评论
快来发表一下你的评论吧 !