STM32内存笔记

发布时间:2026/6/28 5:24:01
STM32内存笔记 (深入理解计算机系统) bss段data段、text段、堆(heap)和栈(stack) - 跑马灯的忧伤 - 博客园keil 编译后codeRO-data,RW-data,ZI-data含义及mcu的flash实际存储数据bss段bss段bss segment通常是指用来存放程序中未初始化的全局变量的一块内存区域。bss是英文Block Started by Symbol的简称。bss段属于静态内存分配。data段数据段data segment通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。text段代码段code segment/text segment通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定并且内存区域通常属于只读(某些架构也允许代码段为可写即允许修改程序)。在代码段中也有可能包含一些只读的常数变量例如字符串常量等。堆heap堆是用于存放进程运行中被动态分配的内存段它的大小并不固定可动态扩张或缩减。当进程调用malloc等函数分配内存时新分配的内存就被动态添加到堆上堆被扩张当利用free等函数释放内存时被释放的内存从堆中被剔除堆被缩减。栈(stack)栈又称堆栈是用户存放程序临时创建的局部变量也就是说我们函数括弧“{}”中定义的变量但不包括static声明的变量static意味着在数据段中存放变量。除此以外在函数被调用时其参数也会被压入发起调用的进程栈中并且待到调用结束后函数的返回值也会被存放回栈中。由于栈的先进先出(FIFO)特点所以栈特别方便用来保存/恢复调用现场。从这个意义上讲我们可以把堆栈看成一个寄存、交换临时数据的内存区。一个程序本质上都是由 bss段、data段、text段三个组成的。这样的概念不知道最初来源于哪里的规定但在当前的计算机程序设计中是很重要的一个基本概念。而且在嵌入式系统的设计中也非常重要牵涉到嵌入式系统运行时的内存大小分配存储单元占用空间大小的问题。在采用段式内存管理的架构中比如intel的80x86系统bss段通常是指用来存放程序中未初始化的全局变量的一块内存区域一般在初始化时bss 段部分将会清零。bss段属于静态内存分配即程序一开始就将其清零了。比如在C语言之类的程序编译完成之后已初始化的全局变量保存在.data 段中未初始化的全局变量保存在.bss 段中。text和data段都在可执行文件中在嵌入式系统里一般是固化在镜像文件中由系统从可执行文件中加载而bss段不在可执行文件中由系统初始化。【例】两个小程序如下程序1:int ar[30000]; void main() { ...... } 程序2:int ar[300000] {1, 2, 3, 4, 5, 6 }; void main() { ...... }发现程序2编译之后所得的.exe文件比程序1的要大得多。当下甚为不解于是手工编译了一下并使用了/FAs编译选项来查看了一下其各自的.asm发现在程序1.asm中ar的定义如下_BSS SEGMENT?ar3PAHA DD 0493e0H DUP (?) ; ar_BSS ENDS而在程序2.asm中ar被定义为_DATA SEGMENT?ar3PAHA DD 01H ; arDD 02HDD 03HORG $1199988_DATA ENDS区别很明显一个位于.bss段而另一个位于.data段两者的区别在于全局的未初始化变量存在于.bss段中具体体现为一个占位符全局的已初始化变量存于.data段中而函数内的自动变量都在栈上分配空间.bss是不占用.exe文件空间的其内容由操作系统初始化清零.data却需要占用其内容由程序初始化。因此造成了上述情况。bss段未手动初始化的数据并不给该段的数据分配空间只是记录数据所需空间的大小bss段的大小从可执行文件中得到 然后链接器得到这个大小的内存块紧跟在数据段后面。data段已手动初始化的数据则为数据分配空间数据保存在目标文件中data段包含经过初始化的全局变量以及它们的值。当这个内存区进入程序的地址空间后全部清零。包含data段和bss段的整个区段此时通常称为数据区。STM32的内存管理和堆栈相关的认知今天仔细读了一下内存管理的代码然后还有看了堆栈的相关知识把以前不太明白的一些东西想通了写下来方便以后查看也想大家看了能指出哪里不对然后修改。首先先看一下stm32的存储器结构。​FlashSRAM寄存器和输入输出端口被组织在同一个4GB的线性地址空间内。可访问的存储器空间被分成8个主要块每个块为512MB。FLASH存储下载的程序。SRAM是存储运行程序中的数据。所以只要你不外扩存储器写完的程序中的所有东西也就会出现在这两个存储器中。这是一个前提堆栈的认知1. STM32中的堆栈。这个我产生过混淆导致了很多逻辑上的混乱。首先要说明的是单片机是一种集成电路芯片集成CPU、RAM、ROM、多种I/O口和中断系统、定时器/计数器等功能。CPU中包括了各种总线电路计算电路逻辑电路还有各种寄存器。Stm32有通用寄存器 R0‐ R15 以及一些特殊功能寄存器,其中包括了堆栈指针寄存器。当stm32正常运行程序的时候来了一个中断CPU就需要将寄存器中的值压栈到RAM里然后将数据所在的地址存放在堆栈寄存器中。等中断处理完成退出时再将数据出栈到之前的寄存器中这个在C语言里是自动完成的。2. 编程中的堆栈。在编程中很多时候会提到堆栈这个东西准确的说这个就是RAM中的一个区域。我们先来了解几个说明:(1) 程序中的所有内容最终只会出现在flashram里不外扩。(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的全局变量。FlashCode RO Data RW Data;RAM RW-dataZI-data;这个是MDK编译之后能够得到的每个段的大小也就能得到占用相应的FLASH和RAM的大小但是还有两个数据段也会占用RAM但是是在程序运行的时候才会占用那就是堆和栈。在stm32的启动文件.s文件里面就有堆栈的设置其实这个堆栈的内存占用就是在上面RAM分配给RW-dataZI-data之后的地址开始分配的。堆:是编译器调用动态内存分配的内存区域。栈:是程序运行的时候局部变量的地方所以局部变量用数组太大了都有可能造成栈溢出。堆栈的大小在编译器编译之后是不知道的只有运行的时候才知道所以需要注意一点就是别造成堆栈溢出了。。。不然就等着hardfault找你吧。3. OS中的堆栈及其内存管理。嵌入式系统的堆栈不管是用什么方法来得到内存感觉他的方式都和编程中的堆差不多。目前我知道两种获得内存情况1用庞大的全局变量数组来圈住一块内存然后将这个内存拿来进行内存管理和分配。这种情况下堆栈占用的内存就是上面说的如果没有初始化数组或者数组的初始化值为0堆栈就是占用的RAM的ZI-data部分如果数组初始化值不为0堆栈就占用的RAM的RW-data部分。这种方式的好处是容易从逻辑上知道数据的来由和去向。2​就是把编译器没有用掉的RAM部分拿来做内存分配也就是除掉RW-dataZI-data编译器堆编译器栈后剩下的RAM内存中的一部分或者全部进行内存管理和分配。这样的情况下就只需要知道内存剩下部分的首地址和内存的尾地址然后要用多少内存就用首地址开始挖做一个链表把内存获取和释放相关信息链接起来就能及时的对内存进行管理了。内存管理的算法多种多样不详说这样的情况下OS的内存分配和自身局部变量或者全局变量不冲突之前我就在这上面纠结了很久以为函数里面的变量也是从系统的动态内存中得来的。这种方式感觉更加能够明白自己地址的开始和结束。这两种方法我感觉没有谁更高明因为只是一个内存的获取方式高明的在于内存的管理和分配。​keil编译后codeRO-data,RW-data,ZI-data含义及mcu的flash实际存储数据keil编译后会有一行Program Size:CodexxxRO-dataxxx RW-dataxxx ZI-dataxxxCode 代表执行的代码程序中所有的函数都位于此处。RO-data 代表只读数据程序中所定义的全局常量数据和字符串都位于此处。RW-data 代表已初始化的读写数据程序中定义并且初始化的全局变量和静态变量位于此处。ZI-data 代表未初始化的读写数据程序中定义了但没有初始化的全局变量和静态变量位于此处。ZI英语是zero initial就是程序中用到的变量并且被系统初始化为0的变量的字节数keil编译器默认是把你没有初始化的变量都赋值一个0这些变量在程序运行时是保存在RAM中的。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程序才能正常访问变量。否则只能运行不含变量的代码。