[应用相关]

堆栈的认知

[复制链接]
716|11
手机看帖
扫描二维码
随时随地手机跟帖
guanjiaer|  楼主 | 2018-8-18 14:03 | 显示全部楼层 |阅读模式

1.     STM32中的堆栈。

这个我产生过混淆,导致了很多逻辑上的混乱。首先要说明的是单片机是一种集成电路芯片,集成CPU、RAM、ROM、多种I/O口和中断系统、定时器/计数器等功能。CPU中包括了各种总线电路,计算电路,逻辑电路,还有各种寄存器。Stm32有通用寄存器R0‐R15 以及一些特殊功能寄存器,其中包括了堆栈指针寄存器。当stm32正常运行程序的时候,来了一个中断,CPU就需要将寄存器中的值压栈到RAM里,然后将数据所在的地址存放在堆栈寄存器中。等中断处理完成退出时,再将数据出栈到之前的寄存器中,这个在C语言里是自动完成的。

2.     编程中的堆栈。

在编程中很多时候会提到堆栈这个东西,准确的说这个就是RAM中的一个区域。我们先来了解几个说明:

(1) 程序中的所有内容最终只会出现在flash,ram里(不外扩)。

(2) 段的划分,是将类似数据种类存储在一个区域里,方便管理,但正如上面所说,不管什么段的数据,都是最终在flash和ram里面。

C语言上分为栈、堆、bss、data、code段。具体每个段具体是存储什么数据的,直接百度吧。重点分析一下STM32以及在MDK里面段的划分。

MDK下Code,RO-data,RW-data,ZI-data这几个段:

Code是存储程序代码的。

​RO-data是存储const常量和指令。

​RW-data是存储初始化值不为0的全局变量。

​ZI-data是存储未初始化的全局变量或初始化值为0的全局变量。

Flash=Code + RO-Data + RW-Data;

RAM= RW-data+ZI-data;

这个是MDK编译之后能够得到的每个段的大小,也就能得到占用相应的FLASH和RAM的大小,但是还有两个数据段也会占用RAM,但是是在程序运行的时候,才会占用,那就是堆和栈。在stm32的启动文件.s文件里面,就有堆栈的设置,其实这个堆栈的内存占用就是在上面RAM分配给RW-data+ZI-data之后的地址开始分配的。

堆:是编译器调用动态内存分配的内存区域。

栈:是程序运行的时候局部变量的地方,所以局部变量用数组太大了都有可能造成栈溢出。

堆栈的大小在编译器编译之后是不知道的,只有运行的时候才知道,所以需要注意一点,就是别造成堆栈溢出了。。。不然就等着hardfault找你吧。

3.     OS中的堆栈及其内存管理。

嵌入式系统的堆栈,不管是用什么方法来得到内存,感觉他的方式都和编程中的堆差不多。目前我知道两种获得内存情况:

(1)用庞大的全局变量数组来圈住一块内存,然后将这个内存拿来进行内存管理和分配。这种情况下,堆栈占用的内存就是上面说的:如果没有初始化数组,或者数组的初始化值为0,堆栈就是占用的RAM的ZI-data部分;如果数组初始化值不为0,堆栈就占用的RAM的RW-data部分。这种方式的好处是容易从逻辑上知道数据的来由和去向。

(2)​就是把编译器没有用掉的RAM部分拿来做内存分配,也就是除掉RW-data+ZI-data+编译器堆+编译器栈后剩下的RAM内存中的一部分或者全部进行内存管理和分配。这样的情况下就只需要知道内存剩下部分的首地址和内存的尾地址,然后要用多少内存,就用首地址开始挖,做一个链表,把内存获取和释放相关信息链接起来,就能及时的对内存进行管理了。内存管理的算法多种多样,不详说,这样的情况下:OS的内存分配和自身局部变量或者全局变量不冲突,之前我就在这上面纠结了很久,以为函数里面的变量也是从系统的动态内存中得来的。这种方式感觉更加能够明白自己地址的开始和结束。

这两种方法我感觉没有谁更高明,因为只是一个内存的获取方式,高明的在于内存的管理和分配。


keil编译后会有一行:Program Size:Code=xxxRO-data=xxxRW-data=xxxZI-data=xxx

Code 代表执行的代码,程序中所有的函数都位于此处。

RO-data 代表只读数据,程序中所定义的全局常量数据和字符串都位于此处。

RW-data 代表已初始化的读写数据,程序中定义并且初始化的全局变量和静态变量位于此处。

ZI-data 代表未初始化的读写数据,程序中定义了但没有初始化的全局变量和静态变量位于此处。ZI英语是zero initial,就是程序中用到的变量并且被系统初始化为0的变量的字节数,keil编译器默认是把你没有初始化的变量都赋值一个0,这些变量在程序运行时是保存在RAM中的。

Code: 程序所占用的FLASH大小,存储在FLASH.

RO-data: Read-only-data,程序定义的常量,存储在FLASH中。

RW-data:Read-write-data,已经被初始化的变量,存储在SRAM中。

ZI-data:Zero-Init-data,未被初始化的变量,存储在SRAM中。


2.如果你查看.map文件,如下例子:

==============================================================================

    Total RO  Size (Code + RO Data)                 2980 (   2.91kB)
    Total RW  Size (RW Data + ZI Data)               104 (   0.10kB)
    Total ROM Size (Code + RO Data + RW Data)       2988 (   2.92kB)

==============================================================================

Total ROM Size (Code + RO Data + RW Data)这样所写的程序占用的ROM的字节总数,也就是说程序所下载到ROM flash 中的大小。为什么Rom中还要存RW,因为掉电后RAM中所有数据都丢失了,每次上电RAM中的数据是被重新赋值的,每次这些固定的值就是存储在Rom中的,为什么不包含ZI段呢,是因为ZI数据都是0,没必要包含,只要程序运行之前将ZI数据所在的区域一律清零即可,包含进去反而浪费存储空间。

实际上,ROM中的指令至少应该有这样的功能:
       1. 将RW从ROM中搬到RAM中,因为RW是变量,变量不能存在ROM中。
       2. 将ZI所在的RAM区域全部清零,因为ZI区域并不在Image中,所以需要程序根据编译器给出的ZI地址及大小来将相应得RAM区域清零。ZI中也是变量,同理:变量不能存在ROM中。
       在程序运行的最初阶段,RO中的指令完成了这两项工作后C程序才能正常访问变量。否则只能运行不含变量的代码。


dingbo95| | 2018-8-19 19:57 | 显示全部楼层
总结性很强,还是在多次实践后才懂这些文字的意义。

使用特权

评论回复
zhanzr21| | 2018-8-19 22:37 | 显示全部楼层
理解的很深刻.

不同OS管理内存的方式不同, 同一种OS管理内存也有不同方式.
比如FreeRTOS从9.0.0开始有6种内存管理方式, 还可以自扩展.

文中说"编译后不知道堆栈大小", 这个有点歧义. 事实上工具是知道堆栈大小的, 这个是程序员配置的. C Lib的初始化时要初始化堆栈的. 只是嵌入式的C Runtime不做堆栈边界检查. 对于Stack, 溢出了可能发生Hardfauit,也有可能不发生HardFault, 对于堆, malloc返回失败.

总之这个问题很值得深入研究, 为楼主的研究精神点赞!

使用特权

评论回复
观海| | 2018-8-20 10:59 | 显示全部楼层
有没有列和表啊

使用特权

评论回复
guanjiaer|  楼主 | 2018-8-20 11:03 | 显示全部楼层
观海 发表于 2018-8-20 10:59
有没有列和表啊

木有 那只是你自己运用的一种数据结构

使用特权

评论回复
labasi| | 2018-8-20 12:06 | 显示全部楼层
详细而深刻 感谢分享

使用特权

评论回复
wakayi| | 2018-8-20 12:50 | 显示全部楼层
理论上还是明白的 但是我本人 真的很头疼那些地址

使用特权

评论回复
观海| | 2018-8-21 09:22 | 显示全部楼层
guanjiaer 发表于 2018-8-20 11:03
木有 那只是你自己运用的一种数据结构

哦好的 谢谢 知道了

使用特权

评论回复
天灵灵地灵灵| | 2018-8-21 09:34 | 显示全部楼层
知道了那些缩写后,就容易懂了。

使用特权

评论回复
天灵灵地灵灵| | 2018-8-21 09:34 | 显示全部楼层
知道了缩写就懂了。

使用特权

评论回复
木木guainv| | 2018-8-21 12:19 | 显示全部楼层
非常感谢楼主分享

使用特权

评论回复
Richardd| | 2018-8-23 17:20 | 显示全部楼层
堆栈的大小在编译器编译之后是不知道的,只有运行的时候才知道

使用特权

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

本版积分规则

72

主题

3836

帖子

2

粉丝