精心總結
首先
一個程序被加載到內存中,這塊內存首先就存在兩種屬性:靜態分配內存和動態分配內存。
靜態分配內存:是在程序編譯和鏈接時就確定好的內存。
動態分配內存:是在程序加載、調入、執行的時候分配/回收的內存。
任何一個程序本質上都是由 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程序才能正常訪問變量。否則只能運行不含變量的代碼。