你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32 内存分配解析及变量的存储位置

[复制链接]
STMCU小助手 发布时间:2022-1-18 22:46
内存映射
在一些桌面程序中,整个内存映射是通过虚拟内存来进行管理的,使用一种称为内存管理单元(MMU)的硬件结构来将程序的内存映射到物理RAM。在对于 RAM 紧缺的嵌入式系统中,是缺少 MMU 内存管理单元的。因此在一些嵌入式系统中,比如常用的 STM32 来讲,内存映射被划分为闪存段(也被称为Flash,用于存储代码和只读数据)和RAM段,用于存储读写数据。
STM32 的 Flash 和 RAM 地址范围
笔者标题所说的内存是指 STM32 的 Flash 和 RAM,下图是 ARM Cortex M3 的地址映射图:4 ]4 z7 P1 Y3 [: d
* j: I: Y6 `3 u( A- K
从图中我们可以看到 RAM 地址是从 0x2000 0000 开始的,Flash地址是从 0x0800 0000 开始的,笔者将在下文中着重对这两部分进行剖析。
Flash#
代码和数据是存放在 flash 中的,下面是将 flash 内部进行细分之后的一张图,图中标明了代码段,数据段以及常量在 flash 中的位置。
6 k  G* p; G2 {7 w; X& w. |' Q# ?( ?& V! J1 s/ F# S  N& h
如上图所示,Flash 又可以细分为这么几个部分,分别是文本段 (Text),其中文本段中又包含可执行代码 (Executable Code)和常量 (Literal Value),在文本段之后就是只读数据区域 (Read Only Data),当然并不是所有架构的单片机都满足这样一个排布规律,这里只针对ARM Cortex M3 系列,只读数据段后面接着的就是数据复制段 (Copy of Data Section),第一次遇到这个概念的朋友看到数据复制可能会有所疑惑,其实这个段充当的作用是存放程序中初始化为非 0 值的全局变量的初始值,之所以要将初始值存放到这里,是因为全局变量是存放在 RAM 上的,RAM 上的值掉电便丢失,每次上电后这些变量是要进行重新赋值的,而重新赋的值就存放在这里。那为什么不存放初始化为 0 的全局变量初始值呢,原因也很简单,既然是初始化为 0,那么在上电后统一对存放初始化为 0 的全局变量的那块区域清0就好了。下面举一个例子分析各个变量在上述中的存储位置:
! M3 f+ c( }6 G8 e, l/ {

: e( N( R2 M7 r& T) W1 Z
在上述代码中,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会被链接文件“分区”为如下几个段:
7 p7 K/ V+ Q. @( W; v! i" W0 R7 V$ c# j) b! k% l4 y
如上图所示,RAM 中包含了如下几个部分:
  • 栈 (Stack) : 存放局部变量和函数调用时的返回地址
  • 堆 (heap) : 由 malloc 申请,由 free 释放
  • bss : 存放未初始化或者是初始化为 0 的全局变量
  • data : 存放初始化为非 0 值的全局变量
    # s8 v9 m* h* P% r
下面举一个简单的例子来说明变量在各个段中的存储位置:
  1. #include <stdio.h># t+ h; h, s- B. `
  2. #include <stdlib.h>& ]! o/ }% m7 F: [  J' W
  3. int data_var = 500;, f4 R8 X  X; L1 J0 Q2 Y
  4. int bss_var0;7 N  t  y8 M5 \8 U2 |5 {6 W
  5. int bss_var1 = 0;( f. E3 B) G/ T2 k; r
  6. static int static_var;/ f5 }% G' S( W" C  q$ B
  7. - J* a9 g0 ?6 U' z
  8. void my_function(void)+ S2 t, Z" W4 m4 c% `7 m9 D8 L; a
  9. {4 M; z. A8 I3 O8 ^. U: A6 U
  10.         static int static_var1 = 0;1 e( M' k' S; X  R( m
  11.         int stack = 0;9 r- Y. I/ n! j; r- f" K1 w% B2 I
  12.         char *buffer;' B* a# A' t2 G: m9 Y) o4 \- u
  13.         const int value = 1;
    % \2 t: l3 m9 Y  |; O( |
  14.         buffer = malloc(10);
    / {" i( S9 y  c( w
  15. }
复制代码
C
5 A" q, o" t- n1 h! m' Z  u
上述变量的命名已经很清楚地表明了变量处于 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 文件的最底部有这么一个信息:
+ R, Y% e, d' S4 A, v6 n* L$ \5 f; G) W& I/ J# @  H
上图中的各个段是和上文所述是能够进行对应起来的,正如下面这张表所示:
CodeRO DataRW DataZI Data/ c/ b" l$ j6 t0 R- Y  q
Executable CodeRead Only Datadatabss
; ~8 ~, F2 m- G3 k' D- M0 h& F总结
对于 RAM 和 flash 空间都有限的 MCU 来讲,了解各个变量在内存中的存储位置是很有必要的,他能够很好地帮助我们去解决很多问题。
% E) x4 D  |1 J& I- h( c  Y
收藏 评论0 发布时间:2022-1-18 22:46

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版