STM32内存分配解析及变量的存储位置

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

STM32内存分配解析及变量的存储位置
原⽂
内存映射
在⼀些桌⾯程序中,整个内存映射是通过虚拟内存来进⾏管理的,使⽤⼀种称为内存管理单元(MMU)的硬件结构来将程序的内存映射到物理RAM。

在对于 RAM 紧缺的嵌⼊式系统中,是缺少 MMU 内存管理单元的。

因此在⼀些嵌⼊式系统中,⽐如常⽤的 STM32 来讲,内存映射被划分为闪存段(也被称为Flash,⽤于存储代码和只读数据)和RAM段,⽤于存储读写数据。

STM32 的 Flash 和 RAM 地址范围
笔者标题所说的内存是指 STM32 的 Flash 和 RAM,下图是 ARM Cortex M3 的地址映射图:
从图中我们可以看到 RAM 地址是从 0x2000 0000 开始的,Flash地址是从 0x0800 0000 开始的,笔者将在下⽂中着重对这两部分进⾏剖析。

Flash
代码和数据是存放在 flash 中的,下⾯是将 flash 内部进⾏细分之后的⼀张图,图中标明了代码段,数据段以及常量在 flash 中的位置。

如上图所⽰,Flash ⼜可以细分为这么⼏个部分,分别是⽂本段 (Text),其中⽂本段中⼜包含可执⾏代码 (Executable Code)和常量 (Literal Value),在⽂本段之后就是只读数据区域 (Read Only Data),当然并不是所有架构的单⽚机都满⾜这样⼀个排布规律,这⾥只针对ARM Cortex M3 系列,只读数据段后⾯接着的就是数据复制段 (Copy of Data Section),第⼀次遇到这个概念的朋友看到数据复制可能会有所疑惑,其实这个段充当的作⽤是存放程序中初始化为⾮ 0 值的全局变量的初始值,之所以要将初始值存放到这⾥,是因为全局变量是存放在 RAM 上的,RAM 上的值掉电便丢失,每次上电后这些变量是要进⾏重新赋值的,⽽重新赋的值就存放在这⾥。

那为什么不存放初始化为 0 的全局变量初始值呢,原因也很简单,既然是初始化为 0,那么在上电后统⼀对存放初始化为 0 的全局变量的那块区域清0就好了。

下⾯举⼀个例⼦分析各个变量在上述中的存储位置:
#include <stdio.h>
const int read_only_variable = 2000;
int data = 500;
void my_function(void)
{
int x = 200;
char *str = "string";
}
在上述代码中,read_only_variable 是⼀个⽤ const 修饰的全局变量,它是只读的,存放在 flash 中的只读数据区域,编译器会给 read_only_variable 分配⼀个地址,并将 2000这个数据存放到这个位置。

data 这个变量将存放到 RAM 中的RW区域中 (后⾯将会进⾏详细讲解),但是 data 后⾯的初始值 500 将会被存放到数据复制区域中,也就是上图中从下往上的第三个区域。

在 my_function 中的变量 x 将会被存放到 RAM 中的堆栈中,将 x 赋值为 200 ,200 将被存储到 flash ⾥的 Text 中的常量区 (Literal Valu) 中。

str 是⼀个char 型的指针变量,它指向的是字符串第⼀个字符存放的位置,然⽽对于字符串 string 来讲,它是存放在Text常量区的,所以指针变量指向这个区域的⼀个地址,但是因为它终归中局部变量,它指向 Flash 的⼀个地址,但是其本⾝还是存放于 RAM 中的堆栈上的。

RAM
STM32单⽚机的⽚内RAM会被链接⽂件“分区”为如下⼏个段:
如上图所⽰,RAM 中包含了如下⼏个部分:
栈 (Stack) : 存放局部变量和函数调⽤时的返回地址
堆 (heap) : 由 malloc 申请,由 free 释放
bss : 存放未初始化或者是初始化为 0 的全局变量
data : 存放初始化为⾮ 0 值的全局变量
下⾯举⼀个简单的例⼦来说明变量在各个段中的存储位置:
#include <stdio.h>
#include <stdlib.h>
int data_var = 500;
int bss_var0;
int bss_var1 = 0;
static int static_var;
void my_function(void)
{
static int static_var1 = 0;
int stack = 0;
char *buffer;
const int value = 1;
buffer = malloc(10);
}
上述变量的命名已经很清楚地表明了变量处于 RAM 中的哪⼀个段,data_var 是已经初始化的全局变量,存放在 RAM 的 data 区,bss_var0 和 bss_var1是未初始化和初始化为0的全局变量,他们都存放于 RAM 中的 bss段,由 static 修饰的static_var 和 static_var1 都存放于 bss段,区别只在于两个变量的作⽤域不同。

stack 是在函数内部定义的局部变量,其存放于 RAM 的栈区域,⽤ const 修饰的局部变量 value ,虽然他是只读的,但是它是存储于 RAM 中的栈中的,这⾥也说明⼀点,并不是所有⽤ const 修饰的变量都是存放于只读变量区的。

buffer指针变量⽤ malloc 函数申请了 10 字节的内存空间,那这10字节的内存空间位于堆中。

堆栈溢出
如果在程序运⾏的过程中,堆的空间也⼀直在消耗,同时栈的空间也在增加,那么这时堆和栈如果碰到⼀起,那么就会造成堆栈溢出,从⽽导致我们的程序跑飞。

STM32中的map⽂件分析
在⽤ keil 编译 STM32 ⼯程之后,我们会得到⼀个 map ⽂件,map ⽂件的最底部有这么⼀个信息:
上图中的各个段是和上⽂所述是能够进⾏对应起来的,正如下⾯这张表所⽰:
Code RO Data RW Data ZI Data Executable Code Read Only Data data bss
总结
对于 RAM 和 flash 空间都有限的 MCU 来讲,了解各个变量在内存中的存储位置是很有必要的,他能够很好地帮助我们去解决很多问题。

相关文档
最新文档