STM32 內(nèi)存分配解析及變量的存儲位置
筆者能力有限,如果文中出現(xiàn)不對的地方,還請各位朋友能夠及時地給我指出來,我將不勝感激,謝謝~
內(nèi)存映射
在一些桌面程序中,整個內(nèi)存映射是通過虛擬內(nèi)存來進行管理的,使用一種稱為內(nèi)存管理單元(MMU)的硬件結(jié)構(gòu)來將程序的內(nèi)存映射到物理RAM。在對于 RAM 緊缺的嵌入式系統(tǒng)中,是缺少 MMU 內(nèi)存管理單元的。因此在一些嵌入式系統(tǒng)中,比如常用的 STM32 來講,內(nèi)存映射被劃分為閃存段(也被稱為Flash,用于存儲代碼和只讀數(shù)據(jù))和RAM段,用于存儲讀寫數(shù)據(jù)。
STM32 的 Flash 和 RAM 地址范圍
筆者標題所說的內(nèi)存是指 STM32 的 Flash 和 RAM,下圖是 ARM Cortex M3 的地址映射圖:從圖中我們可以看到 RAM 地址是從 0x2000 0000 開始的,F(xiàn)lash地址是從 0x0800 0000 開始的,筆者將在下文中著重對這兩部分進行剖析。
Flash
代碼和數(shù)據(jù)是存放在 flash 中的,下面是將 flash 內(nèi)部進行細分之后的一張圖,圖中標明了代碼段,數(shù)據(jù)段以及常量在 flash 中的位置。如上圖所示,F(xiàn)lash 又可以細分為這么幾個部分,分別是文本段 (Text),其中文本段中又包含可執(zhí)行代碼 (Executable Code)和常量 (Literal Value),在文本段之后就是只讀數(shù)據(jù)區(qū)域 (Read Only Data),當然并不是所有架構(gòu)的單片機都滿足這樣一個排布規(guī)律,這里只針對ARM Cortex M3 系列,只讀數(shù)據(jù)段后面接著的就是數(shù)據(jù)復制段 (Copy of Data Section),第一次遇到這個概念的朋友看到數(shù)據(jù)復制可能會有所疑惑,其實這個段充當?shù)淖饔檬谴娣懦绦蛑谐跏蓟癁榉?0 值的全局變量的初始值,之所以要將初始值存放到這里,是因為全局變量是存放在 RAM 上的,RAM 上的值掉電便丟失,每次上電后這些變量是要進行重新賦值的,而重新賦的值就存放在這里。那為什么不存放初始化為 0 的全局變量初始值呢,原因也很簡單,既然是初始化為 0,那么在上電后統(tǒng)一對存放初始化為 0 的全局變量的那塊區(qū)域清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 中的只讀數(shù)據(jù)區(qū)域,編譯器會給 read_only_variable 分配一個地址,并將 2000 這個數(shù)據(jù)存放到這個位置。data 這個變量將存放到 RAM 中的RW區(qū)域中 (后面將會進行詳細講解),但是 data 后面的初始值 500 將會被存放到數(shù)據(jù)復制區(qū)域中, 也就是上圖中從下往上的第三個區(qū)域。在 my_function 中的變量 x 將會被存放到 RAM 中的堆棧中,將 x 賦值為 200 ,200 將被存儲到 flash 里的 Text 中的常量區(qū) (Literal Valu) 中。str 是一個 char 型的指針變量,它指向的是字符串第一個字符存放的位置,然而對于字符串 string 來講,它是存放在Text常量區(qū)的,所以指針變量指向這個區(qū)域的一個地址,但是因為它終歸中局部變量,它指向 Flash 的一個地址,但是其本身還是存放于 RAM 中的堆棧上的。
RAM
STM32單片機的片內(nèi)RAM會被鏈接文件“分區(qū)”為如下幾個段:如上圖所示,RAM 中包含了如下幾個部分:
棧 (Stack) : 存放局部變量和函數(shù)調(diào)用時的返回地址
堆 (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);
}
上述變量的命名已經(jīng)很清楚地表明了變量處于 RAM 中的哪一個段,datavar 是已經(jīng)初始化的全局變量,存放在 RAM 的 data 區(qū),bssvar0 和 bssvar1是未初始化和初始化為0的全局變量,他們都存放于 RAM 中的 bss段,由 static 修飾的staticvar 和 static_var1 都存放于 bss段,區(qū)別只在于兩個變量的作用域不同。stack 是在函數(shù)內(nèi)部定義的局部變量,其存放于 RAM 的棧區(qū)域,用 const 修飾的局部變量 value ,雖然他是只讀的,但是它是存儲于 RAM 中的棧中的,這里也說明一點,并不是所有用 const 修飾的變量都是存放于只讀變量區(qū)的。buffer指針變量用 malloc 函數(shù)申請了 10 字節(jié)的內(nèi)存空間,那這10字節(jié)的內(nèi)存空間位于堆中。
堆棧溢出
如果在程序運行的過程中,堆的空間也一直在消耗,同時棧的空間也在增加,那么這時堆和棧如果碰到一起,那么就會造成堆棧溢出,從而導致我們的程序跑飛。
STM32中的map文件分析
在用 keil 編譯 STM32 工程之后,我們會得到一個 map 文件,map 文件的最底部有這么一個信息:上圖中的各個段是和上文所述是能夠進行對應起來的,正如下面這張表所示:
Code | RO Data | RW Data | ZI Data |
---|---|---|---|
Executable Code | Read Only Data | data | bss |
總結(jié)
對于 RAM 和 flash 空間都有限的 MCU 來講,了解各個變量在內(nèi)存中的存儲位置是很有必要的,他能夠很好地幫助我們?nèi)ソ鉀Q很多問題。
您的閱讀是對我最大的鼓勵,您的建議是對我最大地提升,歡迎點擊下方圖片進入小程序進行評論
免責聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!