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_var0bss_var1是未初始化和初始化為0的全局變量,他們都存放于 RAM 中的 bss段,由 static 修飾的static_varstatic_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 來講,了解各個變量在內存中的存儲位置是很有必要的,他能夠很好地幫助我們去解決很多問題。

最后如果您覺得我的文章對您有所幫助,歡迎關注我的個人公眾號:wenzi嵌入式軟件
在這里插入圖片描述

posted @ 2020-04-29 13:37  wenzid  閱讀(...)  評論(...編輯  收藏
全民捕鱼游戏怎么玩