1. 程式人生 > 實用技巧 >stm32堆疊,rom,flash詳細理解

stm32堆疊,rom,flash詳細理解

精心總結

首先

任何一個程式本質上都是由bss段、data段、text段三個組成的。(這句話有點沒清楚)

C語言上分為棧、堆、bss、data、code段。

bss段:

  bss段(bss segment)通常是指用來存放程式中未初始化的全域性變數的一塊記憶體區域。

  bss是英文Block Started by Symbol的簡稱。

  bss段屬於靜態記憶體分配。

data段:

  資料段(data segment)通常是指用來存放程式中已初始化的全域性變數的一塊記憶體區域。

  資料段屬於靜態記憶體分配。

text段:

  程式碼段(code segment/text segment)

通常是指用來存放程式執行程式碼的一塊記憶體區域。

  這部分割槽域的大小在程式執行前就已經確定,並且記憶體區域通常屬於只讀(某些架構也允許程式碼段為可寫,即允許修改程式)。

  在程式碼段中,也有可能包含一些只讀的常數變數,例如字串常量等。

堆(heap):

  堆是用於存放程序執行中被動態分配的記憶體段,它的大小並不固定,可動態擴張或縮減。

  當程序呼叫malloc等函式分配記憶體時,新分配的記憶體就被動態新增到堆上(堆被擴張);

  當利用free等函式釋放記憶體時,被釋放的記憶體從堆中被剔除(堆被縮減)。

棧(stack)

  棧又稱堆疊,是使用者存放程式臨時建立的區域性變數,

  也就是說我們函式括弧“{}”中定義的變數(但不包括static宣告的變數,static意味著在資料段中存放變數)。

  除此以外,在函式被呼叫時,其引數也會被壓入發起呼叫的程序棧中,並且待到呼叫結束後,函式的返回值也會被存放回棧中。

  由於棧的先進後出特點,所以棧特別方便用來儲存/恢復呼叫現場。

  從這個意義上講,我們可以把堆疊看成一個寄存、交換臨時資料的記憶體區。

來看一段程式碼:

#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<string.h>
using
namespace std; static int a=1;//全域性初始化區 int b=2;//全域性初始化區 char *p;//全域性未初始化區 char *p2;//全域性未初始化區,BSS段 int *p3;//全域性未初始化區 ,BSS段 int *p4;//全域性未初始化區 ,BSS段 char *p5={"555555555"};//全域性初始化區 int main(){ static int c=3; int d=4;//記憶體棧 int e=7;//記憶體棧 char *p6={"555555555"}; p=(char*)malloc(sizeof(char)*10);//記憶體堆 p2=(char*)malloc(sizeof(char)*10);//記憶體堆 p3=(int*)malloc(sizeof(int));//記憶體堆 p4=(int*)malloc(sizeof(int)*10);//記憶體堆 for(int i=0;i<=9;i++)p4[i]=0x1; *p3=0x123; strcpy(p,"123456789");//文字常量區 strcpy(p2,"987654321"); strcpy(p2,"123456789"); }

然後回到stm32上面:

Flash,SRAM暫存器和輸入輸出埠被組織在同一個4GB的線性地址空間內。可訪問的儲存器空間被分成8個主要塊,每個塊為512MB,如下圖,SRAM和FLASH在塊1和塊0。

FLASH儲存下載的程式,全域性變數和靜態變數,在執行時,系統會把這些變數搬運到SRAM。同時根據程式碼定義,未初始化的全域性變數在開始執行時會在SRAM開闢一塊空間保證使用。

SRAM是儲存執行程式中的資料,所以,在執行時,我們定義的沒有初始化的全域性變數和沒有初始化的靜態變數,使用的區域性變數,堆疊都放在SRAM中。

所以,只要你不外擴儲存器,寫完的程式中的所有東西也就會出現在這兩個儲存器中。

stm32的堆疊理解:

C語言上分為棧、堆、bss、data、code段。

C語言在微控制器和PC上的記憶體中的區域有一點不同的是,微控制器的code段是直接在flash中的,是可以直接從flash讀取程式碼並執行的,而PC的code段是在記憶體中的,在執行一開始需要從硬碟中將程式碼搬運到記憶體中

MDK下Code, RO-data,RW-data,ZI-data這幾個段:

Code是儲存程式程式碼的。

​RO-data是儲存const常量和指令。

​RW-data是儲存初始化值不為0的全域性變數。

​ZI-data是儲存未初始化的全域性變數或初始化值為0的全域性變數。

所以對應起來stm32中:

Flash=Code + RO Data + RW Data;

RAM= RW-data+ZI-data;

這個是MDK編譯之後能夠得到的每個段的大小,也就能得到佔用相應的FLASH和RAM的大小,但是還有兩個資料段也會佔用RAM,但是是在程式執行的時候,才會佔用,那就是堆和棧。在stm32的啟動檔案.s檔案裡面,就有堆疊的設定,

其實這個堆疊的記憶體佔用就是在上面RAM分配給RW-data+ZI-data之後的地址開始分配的。所以要注意合理的棧大小設定。

OS中的堆疊及其記憶體管理。

嵌入式系統的堆疊,不管是用什麼方法來得到記憶體,感覺他的方式都和程式設計中的堆差不多。目前我知道兩種獲得記憶體情況:

(1)用龐大的全域性變數陣列來圈住一塊記憶體,然後將這個記憶體拿來進行記憶體管理和分配。這種情況下,堆疊佔用的記憶體就是上面說的:如果沒有初始化陣列,或者陣列的初始化值為0,堆疊就是佔用的RAM的ZI-data部分;如果陣列初始化值不為0,堆疊就佔用的RAM的RW-data部分。這種方式的好處是容易從邏輯上知道資料的來由和去向。

(2)​就是把編譯器沒有用掉的RAM部分拿來做記憶體分配,也就是除掉RW-data+ZI-data+編譯器堆+編譯器棧後剩下的RAM記憶體中的一部分或者全部進行記憶體管理和分配。這樣的情況下就只需要知道記憶體剩下部分的首地址和記憶體的尾地址,然後要用多少記憶體,就用首地址開始挖,做一個連結串列,把記憶體獲取和釋放相關資訊連結起來,就能及時的對記憶體進行管理了。記憶體管理的演算法多種多樣,不詳說,這樣的情況下:OS的記憶體分配和自身區域性變數或者全域性變數不衝突。

檢視.map檔案,有如下例子:

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區域並不在RAM中,所以需要程式根據編譯器給出的ZI地址及大小來將相應得RAM區域清零。ZI中也是變數,同理:變數不能存在ROM中。
在程式執行的最初階段,RO中的指令完成了這兩項工作後C程式才能正常訪問變數。否則只能執行不含變數的程式碼。