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)通常是指用來存放程序執行代碼的一塊內存區域。

  這部分區域的大小在程序運行前就已經確定,並且內存區域通常屬於只讀(某些架構也允許代碼段為可寫,即允許修改程序)。

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

注意:

l          BSS區(未初始化數據段):並不給該段的數據分配空間,僅僅是記錄了數據所需空間的大小。

l          DATA(初始化的數據段):為數據分配空間,數據保存在目標文件中。

 

上面這三段內存就組成了我們編寫的程序的本體,但是一個程序運行起來,還需要更多的數據和數據間的交互,否則這個程序就是死的,無用的。所以我們還需要為更多的數據和數據交互提供一塊內存——堆棧。

 

堆(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開辟一塊空間保證使用。(pass:如果

在變量前面加了const限定,那么程序運行直接讀取flash的數據,不會搬到SRAM,這是由於ARM是哈佛結構而非馮諾依曼結構   https://blog.csdn.net/qq_29344757/article/details/75730054   )

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程序才能正常訪問變量。否則只能運行不含變量的代碼。

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM