STM32堆栈区
三、STM32堆棧區(qū)
預(yù)備知識(shí):
一個(gè)由C/C++編譯的程序占用的內(nèi)存分為以下幾個(gè)部分:
l? 棧區(qū)(stack):編譯器自動(dòng)分配釋放,存放函數(shù)的參數(shù)值,局部變量的值等。操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。
l? 堆區(qū)(heap):一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時(shí)可能由OS回收。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式倒是類似于鏈表。
l? 全局區(qū)(靜態(tài)區(qū))(static):全局變量和靜態(tài)變量的存儲(chǔ)是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另 一塊區(qū)域。程序結(jié)束后由系統(tǒng)釋放。
l? 文字常量區(qū) —常量字符串就是放在這里的。 程序結(jié)束后由系統(tǒng)釋放
l? 程序代碼區(qū)—存放函數(shù)體的二進(jìn)制代碼
編譯后,各個(gè)區(qū)存儲(chǔ)內(nèi)容舉例說(shuō)明如下:
//main.cpp
int a = 0; 全局初始化區(qū)
char *p1; 全局未初始化區(qū)
main()
{
int b; 棧
char s[] = “abc”;棧
char *p2; 棧
char *p3 = “123456”;123456\0在常量區(qū),p3在棧上
static int c =0; 全局(靜態(tài))初始化區(qū)
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得來(lái)得10和20字節(jié)的區(qū)域就在堆區(qū)。
strcpy(p1, "123456"); 123456\0放在常量區(qū),編譯器可能會(huì)將它與p3所指向的"123456"
優(yōu)化成一個(gè)地方。
}
STM32的分區(qū)
STM32的分區(qū)從0x2000 0000(0x2000 0000是SRAM的起始地址,由此可知,堆棧等都是在RAM中的)開(kāi)始。靜態(tài)區(qū),堆,棧。所有的全局變量,包括靜態(tài)變量之類的,全部存儲(chǔ)在靜態(tài)存儲(chǔ)區(qū)。?緊跟靜態(tài)存儲(chǔ)區(qū)之后的,是堆區(qū)(如沒(méi)用到malloc,則沒(méi)有該區(qū)),之后是棧區(qū),棧在程序中存儲(chǔ)局部變量。
?
先看啟動(dòng)文件startup_stm32f10x_md.s的定義:
;Amount of memory (in bytes) allocated for Stack?
; Tailor this value to your application needs?
; <h> Stack Configuration?
; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>?
; </h>
Stack_SizeEQU 0x00000400
AREASTACK, NOINIT, READWRITE, ALIGN=3?
Stack_Mem SPACE Stack_Size?
__initial_sp
; <h> Heap Configuration?
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>?
; </h>
Heap_SizeEQU 0x00000200
AREAHEAP, NOINIT, READWRITE, ALIGN=3?
__heap_base?
Heap_Mem SPACE Heap_Size?
__heap_limit
這里定義了堆棧各自大小,堆:512bytes 棧1k;
所以棧區(qū)大小有限制,我們?cè)诰植孔兞恐胁灰x大數(shù)組否則容易溢出。
再看下code ro rw zi
l? Code指存儲(chǔ)到flash【Rom】中的程序代碼。
l? ZI英語(yǔ)是zero initial,就是程序中用到的變量并且被系統(tǒng)初始化為0的變量的字節(jié)數(shù),keil編譯器默認(rèn)是把你沒(méi)有初始化的變量都賦值一個(gè)0,這些變量在程序運(yùn)行時(shí)是保存在RAM中的。
l? RW是可讀可寫變量,就是初始化時(shí)候就已經(jīng)賦值了的,RW + ZI就是你的程序總共使用的RAM字節(jié)數(shù)。
l? RO是程序中的指令和常量,這些值是被保存到Rom中的。
l? Total ROM Size (Code +RO Data + RW Data)這樣所寫的程序占用的ROM的字節(jié)總數(shù),也就是說(shuō)程序所下載到ROM flash 中的大小。為什么Rom中還要存RW,因?yàn)榈綦姾驲AM中所有數(shù)據(jù)都丟失了,每次上電RAM中的數(shù)據(jù)是被重新賦值的,每次這些固定的值就是存儲(chǔ)在Rom中的,為什么不包含ZI段呢,是因?yàn)閆I數(shù)據(jù)都是0,沒(méi)必要包含,只要程序運(yùn)行之前將ZI數(shù)據(jù)所在的區(qū)域一律清零即可。包含進(jìn)去反而浪費(fèi)存儲(chǔ)空間。?
?????????實(shí)際上,ROM中的指令至少應(yīng)該有這樣的功能:?
? ? ? ? ?1. 將RW從ROM中搬到RAM中,因?yàn)镽W是變量,變量不能存在ROM中。?
? ? ? ? ?2. 將ZI所在的RAM區(qū)域全部清零,因?yàn)閆I區(qū)域并不在Image中,所以需要程序根據(jù)編譯器給出的ZI地址及大小來(lái)將相應(yīng)得RAM區(qū)域清零。ZI中也是變量,同理:變量不能存在ROM中。?
?
1,首先來(lái)看:棧(STACK)的問(wèn)題.
函數(shù)的局部變量,都是存放在“棧”里面,棧的英文是:STACK。STACK的大小,我們可以在stm32的啟動(dòng)文件里面設(shè)置,在startup_stm32f10x_hd.s里面,開(kāi)頭就有:
Stack_Size?????EQU???? 0x00000800
表示棧大小是0X800,也就是2048字節(jié),這樣,CPU處理任務(wù)的時(shí)候,函數(shù)局部變量過(guò)多可占用的大小就是:2048字節(jié)。注意:是所有在處理的函數(shù),包括函數(shù)嵌套,遞歸,等等,都是從這個(gè)“棧”里面,來(lái)分配的。
所以,如果一個(gè)函數(shù)的局部變量過(guò)多,比如在函數(shù)里面定義一個(gè)u8 buf[512],這一下就占了1/4的棧大小了,再在其他函數(shù)里面來(lái)搞兩下,程序崩潰是很容易的事情,這時(shí)候,一般你會(huì)進(jìn)入到hardfault....
這是初學(xué)者非常容易犯的一個(gè)錯(cuò)誤.切記不要在函數(shù)里面放N多局部變量,尤其有大數(shù)組的時(shí)候!
對(duì)于棧區(qū),一般棧頂,也就是MSP,在程序剛運(yùn)行的時(shí)候,指向程序所占用內(nèi)存的最高地址。比如附件里面的這個(gè)程序序,內(nèi)存占用如下圖:
圖中,我們可以看到,程序總共占用內(nèi)存:20+2348字節(jié)=2368=0X940
那么程序剛開(kāi)始運(yùn)行的時(shí)候:MSP=0X20000000+0X940=0X2000 0940.
事實(shí)上,也是如此,如圖:
圖中,MSP就是:0X2000 0940,程序運(yùn)行后,MSP就是從這個(gè)地址開(kāi)始,往下給函數(shù)的局部變量分配地址。再說(shuō)說(shuō)棧的增長(zhǎng)方向,我們可以用如下代碼測(cè)試:
//保存棧增長(zhǎng)方向
//0,向下增長(zhǎng);1,向上增長(zhǎng)
static u8 stack_dir;
//查找棧增長(zhǎng)方向,結(jié)果保存在stack_dir里面
void find_stack_direction(void)
{
??? static u8 *addr=NULL;?? //用于存放第一個(gè)dummy的地址。
??? u8dummy;????????????????? //用于獲取棧地址?
??? if(addr==NULL)? ?????? //第一次進(jìn)入
???{??????????????????????????
??????? addr=&dummy;?????? //保存dummy的地址
??????? find_stack_direction ();?//遞歸?
??? }
else???????????????//第二次進(jìn)入?
? {??
???????if(&dummy>addr)stack_dir=1; //第二次dummy的地址大于第一次dummy,那么說(shuō)明棧增長(zhǎng)方向是向上的.?
??????? elsestack_dir=0;?????????? //第二次dummy的地址小于第一次dummy,那么說(shuō)明棧增長(zhǎng)方向是向下的.??
?}
}?
這個(gè)代碼不是我寫的,網(wǎng)上抄來(lái)的,思路很巧妙,利用遞歸,判斷兩次分配給dummy的地址,來(lái)比較棧是向下生長(zhǎng),還是向上生長(zhǎng)。如果你在STM32測(cè)試這個(gè)函數(shù),你會(huì)發(fā)現(xiàn), STM32的棧,是向下生長(zhǎng)的。事實(shí)上,一般CPU的棧增長(zhǎng)方向,都是向下的。
2,再來(lái)說(shuō)說(shuō),堆(HEAP)的問(wèn)題.
全局變量,靜態(tài)變量,以及內(nèi)存管理所用的內(nèi)存,都是屬于“堆"區(qū)”,英文名:“HEAP”,與棧區(qū)不同,堆區(qū),則從內(nèi)存區(qū)域的起始地址開(kāi)始分配給各個(gè)全局變量和靜態(tài)變量。
堆的生長(zhǎng)方向,都是向上的。在程序里面,所有的內(nèi)存分為:堆+棧,只是他們各自的起始地址和增長(zhǎng)方向不同,他們沒(méi)有一個(gè)固定的界限,所以一旦堆棧沖突,系統(tǒng)就到了崩潰的時(shí)候了。同樣,我們用附件里面的例程測(cè)試:
stack_dir的地址是0X2000 0004,也就是STM32的內(nèi)存起始端的地址。
這里本來(lái)應(yīng)該是從0X2000 0000開(kāi)始分配的,但是,我仿真發(fā)現(xiàn)0X2000 0000總是存放:0X2000 0398,這個(gè)值,貌似是MSP,但是又不變化,還請(qǐng)高手幫忙解釋下。其他的,全局變量,則依次遞增,地址肯定大于0X20000004,比如cpu_endian的地址就是0X20000005。這就是STM32內(nèi)部堆的分配規(guī)則.
3,再說(shuō)說(shuō),大小端的問(wèn)題.
大端模式:低位字節(jié)存在高地址上,高位字節(jié)存在低地址上?
小端模式:高位字節(jié)存在高地址上,低位字節(jié)存在低地址上
STM32屬于小端模式,簡(jiǎn)單的說(shuō),比如u32temp=0X12345678;
假設(shè)temp地址在0X2000 0010.
那么在內(nèi)存里面,存放就變成了:
地址?????????????|???????????HEX?????????|
0X2000 0010??|??78?? 56?? 43?12? |
CPU到底是大端還是小端,可以通過(guò)如下代碼測(cè)試:
//CPU大小端
//0,小端模式;1,大端模式.
static u8 cpu_endian;
//獲取CPU大小端模式,結(jié)果保存在cpu_endian里面
void find_cpu_endian(void)
{?
?int x=1;
?if(*(char*)&x==1)cpu_endian=0;?//小端模式?
?else cpu_endian=1;????//大端模式??
}
以上測(cè)試,在STM32上,你會(huì)得到cpu_endian=0,也就是小端模式.
3,最后說(shuō)說(shuō),STM32內(nèi)存的問(wèn)題.
??? 還是以附件工程為例,在前面第一個(gè)圖,程序總共占用內(nèi)存:20+2348字節(jié),這么多內(nèi)存,到底是怎么得來(lái)的呢?
我們可以雙擊Project側(cè)邊欄的:Targt1,會(huì)彈出test.map,在這個(gè)里面,我們就可以清楚的知道這些內(nèi)存到底是怎么來(lái)的了.在這個(gè)test.map最后,Image 部分有:
==============================================================================
Image component sizes
?Code (inc. data)?? ROData??? RW Data??? ZIData????? Debug?? Object Name
??????172????????10????????? ???0????????? ????4????????????0???????995?? delay.o//delay.c里面,fac_us和fac_ms,共占用4字節(jié)
??????112????????12?????????0?????????0?????????0??????? ??427?? led.o
????????72?????????26???????304?????????0??????2048??????? 828??startup_stm32f10x_hd.o? //啟動(dòng)文件,里面定義了Stack_Size為0X800,所以這里是2048.
??????712????????52?????????0?????????0?????????0?????? 2715?? sys.o
???????348???????154?????????0?????????6?????????0???? 208720?? test.o//test.c里面,stack_dir和cpu_endian?以及*addr??,占用6字節(jié).
???????384????????24?????????0?????????8??????? 200??????3050?? usart.o//usart.c定義了一個(gè)串口接收數(shù)組buffer,占用200字節(jié).
???----------------------------------------------------------------------
??????1800???????278???????336????????20?????? 2248????216735?? Object Totals //總共2248+20字節(jié)
????????0?????????0???????? 32?????????0?????????0????????? 0?? (incl.Generated)
????????0?????????0?????????0?????????2????????? 0?????????0?? (incl. Padding)//2字節(jié)用于對(duì)其
???----------------------------------------------------------------------
????? Code (inc. data)?? RO Data???RW Data??? ZI Data?????Debug?? Library Member Name
????????8?????????0?????????0?????????0?????????0???????? 68?? __main.o
??????104?????????0?????????0?????????0?????????0???????? 84?? __printf.o
???????52?????????8?????????0????????? 0?????????0????????? 0??__scatter.o
???????26?????????0?????????0?????????0?????????0????????? 0??__scatter_copy.o
???????28?????????0?????????0?????????0?????????0????????? 0??__scatter_zi.o
???????48?????????6?????????0?????????0?????????0???????? 96??_printf_char_common.o
???????36?????????4?????????0?????????0?????????0???????? 80??_printf_char_file.o
???????92?????????4????????40?????????0?????????0???????? 88??_printf_hex_int.o
??????184?????????0?????????0????????? 0?????????0???????? 88??_printf_intcommon.o
????????0?????????0?????????0?????????0?????????0????????? 0??_printf_percent.o
????????4?????????0?????????0?????????0?????????0????????? 0??_printf_percent_end.o
????????6?????????0?????????0?????????0?????????0????????? 0??_printf_x.o
???????12?????????0?????????0?????????0?????????0???????? 72?? exit.o
????????8?????????0?????????0?????????0?????????0???????? 68?? ferror.o
????????6?????????0?????????0?????????0?????????0??????? 152?? heapauxi.o
????????2?????????0?????????0?????????0?????????0????????? 0?? libinit.o
????????2?????????0?????????0?????????0?????????0????????? 0??libinit2.o
????????2????????? 0?????????0?????????0?????????0????????? 0??libshutdown.o
????????2?????????0?????????0?????????0?????????0????????? 0??libshutdown2.o
?????????8?????????4?????????0?????????0????????96???????? 68??libspace.o????????? //庫(kù)文件(printf使用),占用了96字節(jié)
???????24?????????4?????????0?????????0?????????0???????? 84??noretval__2printf.o
????????0?????????0?????????0?????????0?????????0????????? 0?? rtentry.o
???????12?????????0?????????0?????????0?????????0????????? 0??rtentry2.o
???????? 6?????????0?????????0?????????0?????????0????????? 0??rtentry4.o
????????2?????????0?????????0?????????0?????????0????????? 0?? rtexit.o
???????10?????????0?????????0?????????0?????????0????????? 0?? rtexit2.o
???????74?????????0????????? 0?????????0?????????0???????? 80??sys_stackheap_outer.o
????????2?????????0?????????0?????????0?????????0???????? 68?? use_no_semi.o
????????2?????????0?????????0?????????0?????????0???????? 68??use_no_semi_2.o
??????450?????????8????????? 0?????????0?????????0??????? 236?? faddsub_clz.o
??????388????????76?????????0?????????0?????????0???????? 96?? fdiv.o
???????62?????????4?????????0?????????0?????????0???????? 84?? ffixu.o
???????38?????????0?????????0?????????0????????? 0????????68?? fflt_clz.o
??????258?????????4?????????0?????????0?????????0???????? 84?? fmul.o
??????140?????????4?????????0?????????0?????????0???????? 84?? fnaninf.o
???????10?????????0?????????0?????????0?????????0???????? 68?? fretinf.o
????????0?????????0?????????0?????????0?????????0????????? 0?? usenofp.o
???----------------------------------------------------------------------
?????2118???????126????????42?????????0??????? 100??????1884?? Library Totals? //調(diào)用的庫(kù)用了100字節(jié).
???????10?????????0?????????2?????????0?????????4????????? 0?? (incl.Padding)?? //用于對(duì)其多占用了4個(gè)字節(jié)
???----------------------------------------------------------------------
????? Code (inc. data)?? ROData??? RW Data??? ZIData????? Debug?? Library Name
??????762????????30????????40?????????0???????? 96??????1164?? c_w.l
?????1346????????96?????????0?????????0????????? 0???????720?? fz_ws.l
???----------------------------------------------------------------------
?????2118???????126???????? 42?????????0???????100?????? 1884?? Library Totals
???----------------------------------------------------------------------
==============================================================================
????? Code (inc.data)?? RO Data??? RW Data??? ZIData????? Debug??
?????3918???????404???????378????????20?????? 2348???? 217111??Grand Totals
?????3918???????404???????378????????20?????? 2348???? 217111??ELF Image Totals
?????3918???????404???????378????????20????????? 0?????????0?? ROM Totals
==============================================================================
??? Total RO? Size (Code + ROData)????????????????4296 (?? 4.20kB)
????Total RW? Size (RW Data + ZIData)?????????????2368 (?? 2.31kB)?? //總共占用:2248+20+100=2368.
??? Total ROM Size (Code + RO Data + RWData)?????? 4316 (?? 4.21kB)
==============================================================================
通過(guò)這個(gè)文件,我們就可以分析整個(gè)內(nèi)存,是怎么被占用的,具體到每個(gè)文件,占用多少.一目了然了.
總結(jié)
- 上一篇: 函数入栈顺序
- 下一篇: 802.11协议基础