打印
[应用方案]

单片机堆栈分配

[复制链接]
254|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
nomomy|  楼主 | 2024-1-16 11:55 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Code:表示程序所占用 FLASH 的大小(FLASH)。
RO-data:即 Read Only-data,表示程序定义的常量,如 const 类型(FLASH)。
RW-data:即 Read Write-data,表示已被初始化的全局变量(SRAM)
ZI-data:即 Zero Init-data,表示未被初始化的全局变量(SRAM)

静态数据区(.data和.bss):保存全局变量和static 变量(包括static 全局和局部变量)。全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域(.data), 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bss),程序结束后由系统释放。这些数据也是可读可写的,和stack、heap一样,被包含在sram中。静态区的内容在总个程序的生命周期内都存在,由编译器在编译的时候分配。
(freeRTOS为每个任务分配的栈空间使用的就是静态数据区,ucHeap[ portBYTE_ALIGNMENT ])
堆区(HEAP):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。在单片机的sram中的ZI-data中。由malloc 系列函数或new 操作符分配的内存。其生命周期由free 或delete 决定。在没有释放之前一直存在,直到程序结束。其特点是使用灵活,空间比较大,但容易出错。向上生长;
栈区(STACK):由编译器自动分配释放,在单片机的sram中的ZI-data。保存局部变量。栈上的内容只在函数的范围内存在,当函数运行结束,这些内容也会自动被销毁。其特点是效率高,但空间大小有限。由高地址向低地址生长。

ZI-data的栈空间(Stack)及堆空间(Heap):
在C语言中,函数内部定义的局部变量属于栈空间,进入函数的时候向栈空间申请内存给局部变量,退出时释放局部变量,归还内存空间。而使用malloc动态分配的变量属于堆空间。
在程序中的栈空间和堆空间都是属于ZI-data区域的,这些空间都会被初始值化为0值。编译器给出的ZI-data占用的空间值中包含了堆栈的大小(经实际测试,若程序中完全没有使用malloc动态申请堆空间,编译器会优化,不把堆空间计算在内)。

内存分配方式有三种:
(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。 例如全局变量,static变量。
(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。
栈内存 分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。
动态内存的生存期由我们决定,使用非常灵活,但问题也最多。


堆和栈的大小定义:
定义大小在startup_stm32f4xx.s

Stack_Size                EQU     0x400 //栈的大小为0X400
Heap_Size       EQU     0x800 //堆的大小为0X800

堆和栈地址:
通过MAP文件可知

ucHeap                  0x20000764   Data        7150  heap_4.o(.bss)
HEAP                    0x20002358   Section     2048  startup_stm32f103xb.o(HEAP)
STACK                   0x20002b58   Section     1024  startup_stm32f103xb.o(STACK)

OS所使用的堆大小为 7150个字节,地址为0x20000764,在.bss(静态存储区)
程序堆大小为2048个字节,地址为0x20002358,在堆区
程序栈大小为1024个字节,地址为 0x20002b58,在栈区

静态占用最深的栈空间数量:
在Output目录下,有一个以工程文件命名的后缀为*.bulid_log.htm及*.htm文件其中*.build_log.htm是工程的构建过程日志,而*.htm是链接器生成的静态调用图文件。
在静态调用图文件中包含了整个工程各种函数之间互相调用的关系图,而且它还给出了静态占用最深的栈空间数量以及它对应的调用关系链。

Maximum Stack Usage = 3224 bytes + Unknown(Cycles, Untraceable Function Pointers)
Call chain for Maximum Stack Depth:
StartGPSTask ⇒ nmea_parse ⇒ nmea_parser_push ⇒ nmea_parser_real_push ⇒ nmea_parse_GPRMC ⇒ _nmea_parse_time ⇒ nmea_error ⇒ vsnprintf

该文件说明了本工程的静态栈空间最大占用3224字节(Maximum Stack Usage:3224bytes)
注意这里给出的空间只是静态的栈使用统计,链接器无法统计动态使用情况,例如链接器无法知道递归函数的递归深度。在本文件的后面还可查询到其它函数的调用情况及其它细节。

int a = 0;
char *p1; //全局未初始化区
char s[512];//全局未初始化区
main()
{
        int b; 栈
        char s[] = "abc"; //栈
        char *p2; //栈
        char *p3 = "123456"; //123456\0在常量区,p3在栈上。
        static int c =0; //全局(静态)初始化区
        p1 = (char *)malloc(10);  //堆
        p2 = (char *)malloc(20);  //堆
        strcpy(p1,   "123456");   //123456/0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。   
}

默认1K的栈,指的是call stack(函数调用使用的栈)。这个栈用来存放函数调用时所传递的参数(并非所有参数都通过栈传递),栈指针,return address,寄存器的备份,还有每层函数的local variable。
1K大多数情况都是够用的,如果用完可能会发生非法访问等错误。
可能会用完1K的情况:
某个被调用的函数申请了一个很大的local variable,比如直接在栈上开个大数组之类的;
还有就是递归调用,递归层数多了后很容易就用完1K。所以递归一般不要乱用。
转自https://blog.csdn.net/sinat_31039061/article/details/90183504/

使用特权

评论回复
沙发
花间一壶酒sd| | 2024-8-23 15:51 | 只看该作者
存放已初始化的全局变量或静态变量,这些变量在程序启动时由Flash中的数据初始化,并存放在SRAM中。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

26

主题

1363

帖子

0

粉丝