一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

stm32 棧溢出 錯誤

 wu_wade 2016-06-01

今天搞的一個stm32 的程序發(fā)生了錯誤。全局變量遭到了局部變量的篡改。新手感覺很奇特。

看了一些資料,發(fā)現(xiàn)時棧區(qū)設(shè)置太小所導(dǎo)致的,全局變量向上生長,棧區(qū)向下生長。stm32的棧頂是程序自動生成的(暫時是這么認(rèn)為的,有待進(jìn)一步確定),程序會地洞生成棧頂。并且棧底和全局變量區(qū)是緊挨的,因此如果棧溢出的話,會直接將全局變量去的地址拿來自己用,于是全局變量區(qū)的地址和棧區(qū)的地址重合,導(dǎo)致全局變量遭到局部變量篡改的錯誤。


看看下面一些專業(yè)的解釋會更清晰!

對于單片機這種封閉代碼的運行平臺,內(nèi)存分配有2個大方向,一個是靜態(tài)變量,一個是動態(tài)變量,具體到作用域,又分為局部變量和全局變量.

全局靜態(tài)變量:不管是否調(diào)用,它都在那里,比如LZ示例<test.c>的 line:11 和 line:15,注意這里加了<static>關(guān)鍵字,指明這個變量是并不是真正意義的全局變量,只是在這個文件的所有位置<聲明位置以后的所有位置>可用.

局部靜態(tài)變量:和全局靜態(tài)變量類似,也是不管拉不拉屎先占坑的貨,比如LZ示例<test.c>的 line:23 .特點是加了關(guān)鍵字<static>,意思是在這個位置,它是唯一的.在<find_stack_direction>函數(shù)里使用了遞歸,但局部靜態(tài)變量是不在遞歸里重新分配空間的,原子也是通過這個方式來判斷兩次進(jìn)入之間的地址關(guān)系.

局部動態(tài)變量:這個是最常見的,比如LZ示例<test.c>的 line:24,在這個示例里,每次聲明<神燈啊神燈>,結(jié)果出來的都是新的神燈,許了愿就溜掉,是這種變量的特點.它不會記得它曾經(jīng)是什么.注意,由于每次都喝了孟婆湯,有經(jīng)驗的碼農(nóng)會在召喚時默認(rèn)賦一個初值,避免出現(xiàn)不可預(yù)料的使用.

全局動態(tài)變量:存在嗎?全局可見但又可以踢掉的奇葩嗎?抱歉,這句話對<全局>是個誤解.<全局>的意思是變量本身沒有編譯器指定的生命周期,也就是<作用域>,但還有代碼指定的生命周期.在LZ的示例里,<堆>就是這么一個東西,代碼說<你在>就在,<你不在>就不在.申請了堆后,只要誰(任何位置的代碼)知道這個位置是可以用的,誰都可以用(**具有進(jìn)程內(nèi)存保護的平臺除外**),即使申請空間的變量<掛了>,這個空間也一直存在,直到有代碼把它<銷毀>掉.

順便推銷老帖http://www./posts/list/19693.htm

修改+注釋.
**新的linux把uclinux統(tǒng)一了,不知道是否在單片機實現(xiàn)進(jìn)程內(nèi)存保護,同求證.不過這也不在<封閉代碼平臺>這個前提下了.

一、內(nèi)存基本構(gòu)成  
可編程內(nèi)存在基本上分為這樣的幾大部分:靜態(tài)存儲區(qū)、堆區(qū)和棧區(qū)。他們的功能不同,對他們使用方式也就不同。  

靜態(tài)存儲區(qū):內(nèi)存在程序編譯的時候就已經(jīng)分配好,這塊內(nèi)存在程序的整個運行期間都存在。它主要存放靜態(tài)數(shù)據(jù)、全局?jǐn)?shù)據(jù)和常量。  

棧區(qū):在執(zhí)行函數(shù)時,函數(shù)內(nèi)局部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時這些存儲單元自動被釋放。棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。  

堆區(qū):亦稱動態(tài)內(nèi)存分配。程序在運行的時候用malloc或new申請任意大小的內(nèi)存,程序員自己負(fù)責(zé)在適當(dāng)?shù)臅r候用free或delete釋放內(nèi)存。動態(tài)內(nèi)存的生存期可以由我們決定,如果我們不釋放內(nèi)存,程序?qū)⒃谧詈蟛裴尫诺魟討B(tài)內(nèi)存。 但是,良好的編程習(xí)慣是:如果某動態(tài)內(nèi)存不再使用,需要將其釋放掉,否則,我們認(rèn)為發(fā)生了內(nèi)存泄漏現(xiàn)象。


按照這個說法,我在.s文件里面設(shè)置了:

Heap_Size       EQU     0x00000000

也就是,沒有任何動態(tài)內(nèi)存分配。
這樣,內(nèi)存=靜態(tài)存儲區(qū)+棧區(qū)了。
不存在堆?。?!
因為我沒有用malloc來動態(tài)分配內(nèi)存。
因此,前面提到的一切堆區(qū),其實就是靜態(tài)存儲區(qū)。



棧增長和大端/小端問題是和CPU相關(guān)的兩個問題.

1,首先來看:棧(STACK)的問題.

函數(shù)的局部變量,都是存放在"棧"里面,棧的英文是:STACK.STACK的大小,我們可以在stm32的啟動文件里面設(shè)置,以戰(zhàn)艦stm32開發(fā)板為例,在startup_stm32f10x_hd.s里面,開頭就有:

Stack_Size      EQU     0x00000800

表示棧大小是0X800,也就是2048字節(jié).這樣,CPU處理任務(wù)的時候,函數(shù)局部變量做多可占用的大小就是:2048字節(jié),注意:是所有在處理的函數(shù),包括函數(shù)嵌套,遞歸,等等,都是從這個"棧"里面,來分配的.
所以,如果一個函數(shù)的局部變量過多,比如在函數(shù)里面定義一個u8 buf[512],這一下就占了1/4的棧大小了,再在其他函數(shù)里面來搞兩下,程序崩潰是很容易的事情,這時候,一般你會進(jìn)入到hardfault....
這是初學(xué)者非常容易犯的一個錯誤.切記不要在函數(shù)里面放N多局部變量,尤其有大數(shù)組的時候!

對于棧區(qū),一般棧頂,也就是MSP,在程序剛運行的時候,指向程序所占用內(nèi)存的最高地址.比如附件里面的這個程序序,內(nèi)存占用如下圖:


圖中,我們可以看到,程序總共占用內(nèi)存:20+2348字節(jié)=2368=0X940
那么程序剛開始運行的時候:MSP=0X2000 0000+0X940=0X2000 0940.
事實上,也是如此,如圖:


圖中,MSP就是:0X2000 0940.
程序運行后,MSP就是從這個地址開始,往下給函數(shù)的局部變量分配地址.

再說說棧的增長方向,我們可以用如下代碼測試: 

//保存棧增長方向
//0,向下增長;1,向上增長.
static u8 stack_dir;

//查找棧增長方向,結(jié)果保存在stack_dir里面.
void find_stack_direction(void)
{
    static u8 *addr=NULL; //用于存放第一個dummy的地址。
    u8 dummy;               //用于獲取棧地址 
    if(addr==NULL)    //第一次進(jìn)入
    {                          
        addr=&dummy;     //保存dummy的地址
        find_stack_direction ();  //遞歸 
    }else                //第二次進(jìn)入 
 {  
        if(&dummy>addr)stack_dir=1; //第二次dummy的地址大于第一次dummy,那么說明棧增長方向是向上的. 
        else stack_dir=0;           //第二次dummy的地址小于第一次dummy,那么說明棧增長方向是向下的.  
 }


這個代碼不是我寫的,網(wǎng)上抄來的,思路很巧妙,利用遞歸,判斷兩次分配給dummy的地址,來比較棧是向下生長,還是向上生長.
如果你在STM32測試這個函數(shù),你會發(fā)現(xiàn),STM32的棧,是向下生長的.事實上,一般CPU的棧增長方向,都是向下的.

2,再來說說,堆(HEAP)的問題.

全局變量,靜態(tài)變量,以及內(nèi)存管理所用的內(nèi)存,都是屬于"堆"區(qū),英文名:"HEAP"
與棧區(qū)不同,堆區(qū),則從內(nèi)存區(qū)域的起始地址,開始分配給各個全局變量和靜態(tài)變量.
堆的生長方向,都是向上的.在程序里面,所有的內(nèi)存分為:堆+棧. 只是他們各自的起始地址和增長方向不同,他們沒有一個固定的界限,所以一旦堆棧沖突,系統(tǒng)就到了崩潰的時候了.
同樣,我們用附件里面的例程測試:


stack_dir的地址是0X20000004,也就是STM32的內(nèi)存起始端的地址.
這里本來應(yīng)該是從0X2000 0000開始分配的,但是,我仿真發(fā)現(xiàn)0X2000 0000總是存放:0X2000 0398,這個值,貌似是MSP,但是又不變化,還請高手幫忙解釋下.
其他的,全局變量,則依次遞增,地址肯定大于0X20000004,比如cpu_endian的地址就是0X20000005.
這就是STM32內(nèi)部堆的分配規(guī)則.

3,再說說,大小端的問題.
大端模式:低位字節(jié)存在高地址上,高位字節(jié)存在低地址上 
小端模式:高位字節(jié)存在高地址上,低位字節(jié)存在低地址上

STM32屬于小端模式,簡單的說,比如u32 temp=0X12345678;
假設(shè)temp地址在0X2000 0010.
那么在內(nèi)存里面,存放就變成了:
地址              |            HEX         |
0X2000 0010  |  78   56   43  12  |

CPU到底是大端還是小端,可以通過如下代碼測試:
//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;    //大端模式  
}
以上測試,在STM32上,你會得到cpu_endian=0,也就是小端模式.


3,最后說說,STM32內(nèi)存的問題.
    還是以附件工程為例,在前面第一個圖,程序總共占用內(nèi)存:20+2348字節(jié),這么多內(nèi)存,到底是怎么得來的呢?
我們可以雙擊Project側(cè)邊欄的:Targt1,會彈出test.map,在這個里面,我們就可以清楚的知道這些內(nèi)存到底是怎么來的了.在這個test.map最后,Image 部分有:
==============================================================================
Image component sizes

      Code (inc. data)   RO Data    RW Data    ZI Data      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  //啟動文件,里面定義了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定義了一個串口接收數(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é)用于對其
    ----------------------------------------------------------------------
      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          //庫文件(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)用的庫用了100字節(jié).
        10          0          2          0          4          0   (incl. Padding)   //用于對其多占用了4個字節(jié)
    ----------------------------------------------------------------------
      Code (inc. data)   RO Data    RW Data    ZI Data      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    ZI Data      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 + RO Data)                 4296 (   4.20kB)
    Total RW  Size (RW Data + ZI Data)              2368 (   2.31kB)   //總共占用:2248+20+100=2368.
    Total ROM Size (Code + RO Data + RW Data)       4316 (   4.21kB)
==============================================================================

通過這個文件,我們就可以分析整個內(nèi)存,是怎么被占用的,具體到每個文件,占用多少.一目了然了.

4,最后,看看整個測試代碼:
main.c代碼如下,工程見附件.
#include "sys.h"
#include "usart.h"  
#include "delay.h" 
#include "led.h" 
#include "beep.h"    
#include "key.h"    
//ALIENTEK戰(zhàn)艦STM32開發(fā)板堆棧增長方向以及CPU大小端測試
//保存棧增長方向
//0,向下增長;1,向上增長.
static u8 stack_dir;
//CPU大小端
//0,小端模式;1,大端模式.
static u8 cpu_endian;

//查找棧增長方向,結(jié)果保存在stack_dir里面.
void find_stack_direction(void)
{
    static u8 *addr=NULL; //用于存放第一個dummy的地址。
    u8 dummy;               //用于獲取棧地址 
    if(addr==NULL)    //第一次進(jìn)入
    {                          
        addr=&dummy;     //保存dummy的地址
        find_stack_direction ();  //遞歸 
    }else                //第二次進(jìn)入 
 {  
        if(&dummy>addr)stack_dir=1; //第二次dummy的地址大于第一次dummy,那么說明棧增長方向是向上的. 
        else stack_dir=0;           //第二次dummy的地址小于第一次dummy,那么說明棧增長方向是向下的.  
 }

//獲取CPU大小端模式,結(jié)果保存在cpu_endian里面
void find_cpu_endian(void)

 int x=1;
 if(*(char*)&x==1)cpu_endian=0; //小端模式 
 else cpu_endian=1;    //大端模式  

int main(void)
{    
 Stm32_Clock_Init(9); //系統(tǒng)時鐘設(shè)置
 uart_init(72,9600);   //串口初始化為9600
 delay_init(72);       //延時初始化 
 LED_Init();      //初始化與LED連接的硬件接口  
    printf("stack_dir:%x\r\n",&stack_dir);
    printf("cpu_endian:%x\r\n",&cpu_endian);
 
 find_stack_direction(); //獲取棧增長方式
 find_cpu_endian();  //獲取CPU大小端模式
  while(1)
 {
  if(stack_dir)printf("STACK DIRCTION:向上生長\r\n\r\n");
  else printf("STACK DIRCTION:向下生長\r\n\r\n");
  if(cpu_endian)printf("CPU ENDIAN:大端模式\r\n\r\n");
  else printf("CPU ENDIAN:小端模式\r\n\r\n"); 
  delay_ms(500);
  LED0=!LED0;  
 }  
}
測試結(jié)果如圖:



4、內(nèi)存基本構(gòu)成  
可編程內(nèi)存在基本上分為這樣的幾大部分:靜態(tài)存儲區(qū)、堆區(qū)和棧區(qū)。他們的功能不同,對他們使用方式也就不同。  

靜態(tài)存儲區(qū):內(nèi)存在程序編譯的時候就已經(jīng)分配好,這塊內(nèi)存在程序的整個運行期間都存在。它主要存放靜態(tài)數(shù)據(jù)、全局?jǐn)?shù)據(jù)和常量。  

棧區(qū):在執(zhí)行函數(shù)時,函數(shù)內(nèi)局部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時這些存儲單元自動被釋放。棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。  

堆區(qū):亦稱動態(tài)內(nèi)存分配。程序在運行的時候用malloc或new申請任意大小的內(nèi)存,程序員自己負(fù)責(zé)在適當(dāng)?shù)臅r候用free或delete釋放內(nèi)存。動態(tài)內(nèi)存的生存期可以由我們決定,如果我們不釋放內(nèi)存,程序?qū)⒃谧詈蟛裴尫诺魟討B(tài)內(nèi)存。 但是,良好的編程習(xí)慣是:如果某動態(tài)內(nèi)存不再使用,需要將其釋放掉,否則,我們認(rèn)為發(fā)生了內(nèi)存泄漏現(xiàn)象。


按照這個說法,我在.s文件里面設(shè)置了:

Heap_Size       EQU     0x00000000

也就是,沒有任何動態(tài)內(nèi)存分配。
這樣,內(nèi)存=靜態(tài)存儲區(qū)+棧區(qū)了。
不存在堆!!!
因為我沒有用malloc來動態(tài)分配內(nèi)存。
因此,前面提到的一切堆區(qū),其實就是靜態(tài)存儲區(qū)。



 

今天搞的一個stm32 的程序發(fā)生了錯誤。全局變量遭到了局部變量的篡改。新手感覺很奇特。

看了一些資料,發(fā)現(xiàn)時棧區(qū)設(shè)置太小所導(dǎo)致的,全局變量向上生長,棧區(qū)向下生長。stm32的棧頂是程序自動生成的(暫時是這么認(rèn)為的,有待進(jìn)一步確定),程序會地洞生成棧頂。并且棧底和全局變量區(qū)是緊挨的,因此如果棧溢出的話,會直接將全局變量去的地址拿來自己用,于是全局變量區(qū)的地址和棧區(qū)的地址重合,導(dǎo)致全局變量遭到局部變量篡改的錯誤。


看看下面一些專業(yè)的解釋會更清晰!

對于單片機這種封閉代碼的運行平臺,內(nèi)存分配有2個大方向,一個是靜態(tài)變量,一個是動態(tài)變量,具體到作用域,又分為局部變量和全局變量.

全局靜態(tài)變量:不管是否調(diào)用,它都在那里,比如LZ示例<test.c>的 line:11 和 line:15,注意這里加了<static>關(guān)鍵字,指明這個變量是并不是真正意義的全局變量,只是在這個文件的所有位置<聲明位置以后的所有位置>可用.

局部靜態(tài)變量:和全局靜態(tài)變量類似,也是不管拉不拉屎先占坑的貨,比如LZ示例<test.c>的 line:23 .特點是加了關(guān)鍵字<static>,意思是在這個位置,它是唯一的.在<find_stack_direction>函數(shù)里使用了遞歸,但局部靜態(tài)變量是不在遞歸里重新分配空間的,原子也是通過這個方式來判斷兩次進(jìn)入之間的地址關(guān)系.

局部動態(tài)變量:這個是最常見的,比如LZ示例<test.c>的 line:24,在這個示例里,每次聲明<神燈啊神燈>,結(jié)果出來的都是新的神燈,許了愿就溜掉,是這種變量的特點.它不會記得它曾經(jīng)是什么.注意,由于每次都喝了孟婆湯,有經(jīng)驗的碼農(nóng)會在召喚時默認(rèn)賦一個初值,避免出現(xiàn)不可預(yù)料的使用.

全局動態(tài)變量:存在嗎?全局可見但又可以踢掉的奇葩嗎?抱歉,這句話對<全局>是個誤解.<全局>的意思是變量本身沒有編譯器指定的生命周期,也就是<作用域>,但還有代碼指定的生命周期.在LZ的示例里,<堆>就是這么一個東西,代碼說<你在>就在,<你不在>就不在.申請了堆后,只要誰(任何位置的代碼)知道這個位置是可以用的,誰都可以用(**具有進(jìn)程內(nèi)存保護的平臺除外**),即使申請空間的變量<掛了>,這個空間也一直存在,直到有代碼把它<銷毀>掉.

順便推銷老帖http://www./posts/list/19693.htm

修改+注釋.
**新的linux把uclinux統(tǒng)一了,不知道是否在單片機實現(xiàn)進(jìn)程內(nèi)存保護,同求證.不過這也不在<封閉代碼平臺>這個前提下了.

一、內(nèi)存基本構(gòu)成  
可編程內(nèi)存在基本上分為這樣的幾大部分:靜態(tài)存儲區(qū)、堆區(qū)和棧區(qū)。他們的功能不同,對他們使用方式也就不同。  

靜態(tài)存儲區(qū):內(nèi)存在程序編譯的時候就已經(jīng)分配好,這塊內(nèi)存在程序的整個運行期間都存在。它主要存放靜態(tài)數(shù)據(jù)、全局?jǐn)?shù)據(jù)和常量。  

棧區(qū):在執(zhí)行函數(shù)時,函數(shù)內(nèi)局部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時這些存儲單元自動被釋放。棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。  

堆區(qū):亦稱動態(tài)內(nèi)存分配。程序在運行的時候用malloc或new申請任意大小的內(nèi)存,程序員自己負(fù)責(zé)在適當(dāng)?shù)臅r候用free或delete釋放內(nèi)存。動態(tài)內(nèi)存的生存期可以由我們決定,如果我們不釋放內(nèi)存,程序?qū)⒃谧詈蟛裴尫诺魟討B(tài)內(nèi)存。 但是,良好的編程習(xí)慣是:如果某動態(tài)內(nèi)存不再使用,需要將其釋放掉,否則,我們認(rèn)為發(fā)生了內(nèi)存泄漏現(xiàn)象。


按照這個說法,我在.s文件里面設(shè)置了:

Heap_Size       EQU     0x00000000

也就是,沒有任何動態(tài)內(nèi)存分配。
這樣,內(nèi)存=靜態(tài)存儲區(qū)+棧區(qū)了。
不存在堆?。。?br> 因為我沒有用malloc來動態(tài)分配內(nèi)存。
因此,前面提到的一切堆區(qū),其實就是靜態(tài)存儲區(qū)。



棧增長和大端/小端問題是和CPU相關(guān)的兩個問題.

1,首先來看:棧(STACK)的問題.

函數(shù)的局部變量,都是存放在"棧"里面,棧的英文是:STACK.STACK的大小,我們可以在stm32的啟動文件里面設(shè)置,以戰(zhàn)艦stm32開發(fā)板為例,在startup_stm32f10x_hd.s里面,開頭就有:

Stack_Size      EQU     0x00000800

表示棧大小是0X800,也就是2048字節(jié).這樣,CPU處理任務(wù)的時候,函數(shù)局部變量做多可占用的大小就是:2048字節(jié),注意:是所有在處理的函數(shù),包括函數(shù)嵌套,遞歸,等等,都是從這個"棧"里面,來分配的.
所以,如果一個函數(shù)的局部變量過多,比如在函數(shù)里面定義一個u8 buf[512],這一下就占了1/4的棧大小了,再在其他函數(shù)里面來搞兩下,程序崩潰是很容易的事情,這時候,一般你會進(jìn)入到hardfault....
這是初學(xué)者非常容易犯的一個錯誤.切記不要在函數(shù)里面放N多局部變量,尤其有大數(shù)組的時候!

對于棧區(qū),一般棧頂,也就是MSP,在程序剛運行的時候,指向程序所占用內(nèi)存的最高地址.比如附件里面的這個程序序,內(nèi)存占用如下圖:


圖中,我們可以看到,程序總共占用內(nèi)存:20+2348字節(jié)=2368=0X940
那么程序剛開始運行的時候:MSP=0X2000 0000+0X940=0X2000 0940.
事實上,也是如此,如圖:


圖中,MSP就是:0X2000 0940.
程序運行后,MSP就是從這個地址開始,往下給函數(shù)的局部變量分配地址.

再說說棧的增長方向,我們可以用如下代碼測試: 

//保存棧增長方向
//0,向下增長;1,向上增長.
static u8 stack_dir;

//查找棧增長方向,結(jié)果保存在stack_dir里面.
void find_stack_direction(void)
{
    static u8 *addr=NULL; //用于存放第一個dummy的地址。
    u8 dummy;               //用于獲取棧地址 
    if(addr==NULL)    //第一次進(jìn)入
    {                          
        addr=&dummy;     //保存dummy的地址
        find_stack_direction ();  //遞歸 
    }else                //第二次進(jìn)入 
 {  
        if(&dummy>addr)stack_dir=1; //第二次dummy的地址大于第一次dummy,那么說明棧增長方向是向上的. 
        else stack_dir=0;           //第二次dummy的地址小于第一次dummy,那么說明棧增長方向是向下的.  
 }


這個代碼不是我寫的,網(wǎng)上抄來的,思路很巧妙,利用遞歸,判斷兩次分配給dummy的地址,來比較棧是向下生長,還是向上生長.
如果你在STM32測試這個函數(shù),你會發(fā)現(xiàn),STM32的棧,是向下生長的.事實上,一般CPU的棧增長方向,都是向下的.

2,再來說說,堆(HEAP)的問題.

全局變量,靜態(tài)變量,以及內(nèi)存管理所用的內(nèi)存,都是屬于"堆"區(qū),英文名:"HEAP"
與棧區(qū)不同,堆區(qū),則從內(nèi)存區(qū)域的起始地址,開始分配給各個全局變量和靜態(tài)變量.
堆的生長方向,都是向上的.在程序里面,所有的內(nèi)存分為:堆+棧. 只是他們各自的起始地址和增長方向不同,他們沒有一個固定的界限,所以一旦堆棧沖突,系統(tǒng)就到了崩潰的時候了.
同樣,我們用附件里面的例程測試:


stack_dir的地址是0X20000004,也就是STM32的內(nèi)存起始端的地址.
這里本來應(yīng)該是從0X2000 0000開始分配的,但是,我仿真發(fā)現(xiàn)0X2000 0000總是存放:0X2000 0398,這個值,貌似是MSP,但是又不變化,還請高手幫忙解釋下.
其他的,全局變量,則依次遞增,地址肯定大于0X20000004,比如cpu_endian的地址就是0X20000005.
這就是STM32內(nèi)部堆的分配規(guī)則.

3,再說說,大小端的問題.
大端模式:低位字節(jié)存在高地址上,高位字節(jié)存在低地址上 
小端模式:高位字節(jié)存在高地址上,低位字節(jié)存在低地址上

STM32屬于小端模式,簡單的說,比如u32 temp=0X12345678;
假設(shè)temp地址在0X2000 0010.
那么在內(nèi)存里面,存放就變成了:
地址              |            HEX         |
0X2000 0010  |  78   56   43  12  |

CPU到底是大端還是小端,可以通過如下代碼測試:
//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;    //大端模式  
}
以上測試,在STM32上,你會得到cpu_endian=0,也就是小端模式.


3,最后說說,STM32內(nèi)存的問題.
    還是以附件工程為例,在前面第一個圖,程序總共占用內(nèi)存:20+2348字節(jié),這么多內(nèi)存,到底是怎么得來的呢?
我們可以雙擊Project側(cè)邊欄的:Targt1,會彈出test.map,在這個里面,我們就可以清楚的知道這些內(nèi)存到底是怎么來的了.在這個test.map最后,Image 部分有:
==============================================================================
Image component sizes

      Code (inc. data)   RO Data    RW Data    ZI Data      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  //啟動文件,里面定義了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定義了一個串口接收數(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é)用于對其
    ----------------------------------------------------------------------
      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          //庫文件(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)用的庫用了100字節(jié).
        10          0          2          0          4          0   (incl. Padding)   //用于對其多占用了4個字節(jié)
    ----------------------------------------------------------------------
      Code (inc. data)   RO Data    RW Data    ZI Data      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    ZI Data      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 + RO Data)                 4296 (   4.20kB)
    Total RW  Size (RW Data + ZI Data)              2368 (   2.31kB)   //總共占用:2248+20+100=2368.
    Total ROM Size (Code + RO Data + RW Data)       4316 (   4.21kB)
==============================================================================

通過這個文件,我們就可以分析整個內(nèi)存,是怎么被占用的,具體到每個文件,占用多少.一目了然了.

4,最后,看看整個測試代碼:
main.c代碼如下,工程見附件.
#include "sys.h"
#include "usart.h"  
#include "delay.h" 
#include "led.h" 
#include "beep.h"    
#include "key.h"    
//ALIENTEK戰(zhàn)艦STM32開發(fā)板堆棧增長方向以及CPU大小端測試
//保存棧增長方向
//0,向下增長;1,向上增長.
static u8 stack_dir;
//CPU大小端
//0,小端模式;1,大端模式.
static u8 cpu_endian;

//查找棧增長方向,結(jié)果保存在stack_dir里面.
void find_stack_direction(void)
{
    static u8 *addr=NULL; //用于存放第一個dummy的地址。
    u8 dummy;               //用于獲取棧地址 
    if(addr==NULL)    //第一次進(jìn)入
    {                          
        addr=&dummy;     //保存dummy的地址
        find_stack_direction ();  //遞歸 
    }else                //第二次進(jìn)入 
 {  
        if(&dummy>addr)stack_dir=1; //第二次dummy的地址大于第一次dummy,那么說明棧增長方向是向上的. 
        else stack_dir=0;           //第二次dummy的地址小于第一次dummy,那么說明棧增長方向是向下的.  
 }

//獲取CPU大小端模式,結(jié)果保存在cpu_endian里面
void find_cpu_endian(void)

 int x=1;
 if(*(char*)&x==1)cpu_endian=0; //小端模式 
 else cpu_endian=1;    //大端模式  

int main(void)
{    
 Stm32_Clock_Init(9); //系統(tǒng)時鐘設(shè)置
 uart_init(72,9600);   //串口初始化為9600
 delay_init(72);       //延時初始化 
 LED_Init();      //初始化與LED連接的硬件接口  
    printf("stack_dir:%x\r\n",&stack_dir);
    printf("cpu_endian:%x\r\n",&cpu_endian);
 
 find_stack_direction(); //獲取棧增長方式
 find_cpu_endian();  //獲取CPU大小端模式
  while(1)
 {
  if(stack_dir)printf("STACK DIRCTION:向上生長\r\n\r\n");
  else printf("STACK DIRCTION:向下生長\r\n\r\n");
  if(cpu_endian)printf("CPU ENDIAN:大端模式\r\n\r\n");
  else printf("CPU ENDIAN:小端模式\r\n\r\n"); 
  delay_ms(500);
  LED0=!LED0;  
 }  
}
測試結(jié)果如圖:



4、內(nèi)存基本構(gòu)成  
可編程內(nèi)存在基本上分為這樣的幾大部分:靜態(tài)存儲區(qū)、堆區(qū)和棧區(qū)。他們的功能不同,對他們使用方式也就不同。  

靜態(tài)存儲區(qū):內(nèi)存在程序編譯的時候就已經(jīng)分配好,這塊內(nèi)存在程序的整個運行期間都存在。它主要存放靜態(tài)數(shù)據(jù)、全局?jǐn)?shù)據(jù)和常量。  

棧區(qū):在執(zhí)行函數(shù)時,函數(shù)內(nèi)局部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時這些存儲單元自動被釋放。棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。  

堆區(qū):亦稱動態(tài)內(nèi)存分配。程序在運行的時候用malloc或new申請任意大小的內(nèi)存,程序員自己負(fù)責(zé)在適當(dāng)?shù)臅r候用free或delete釋放內(nèi)存。動態(tài)內(nèi)存的生存期可以由我們決定,如果我們不釋放內(nèi)存,程序?qū)⒃谧詈蟛裴尫诺魟討B(tài)內(nèi)存。 但是,良好的編程習(xí)慣是:如果某動態(tài)內(nèi)存不再使用,需要將其釋放掉,否則,我們認(rèn)為發(fā)生了內(nèi)存泄漏現(xiàn)象。


按照這個說法,我在.s文件里面設(shè)置了:

Heap_Size       EQU     0x00000000

也就是,沒有任何動態(tài)內(nèi)存分配。
這樣,內(nèi)存=靜態(tài)存儲區(qū)+棧區(qū)了。
不存在堆!?。?br> 因為我沒有用malloc來動態(tài)分配內(nèi)存。
因此,前面提到的一切堆區(qū),其實就是靜態(tài)存儲區(qū)。



 


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    青青操精品视频在线观看| 黄色美女日本的美女日人| 精品一区二区三区人妻视频| 久久本道综合色狠狠五月| 极品熟女一区二区三区| 偷拍偷窥女厕一区二区视频| 熟女一区二区三区国产| 欧美日韩国产免费看黄片| 黑人巨大精品欧美一区二区区| 日韩精品视频一二三区| 欧美不卡高清一区二区三区| 隔壁的日本人妻中文字幕版| 欧美野外在线刺激在线观看| 亚洲精品一区二区三区日韩| 日本 一区二区 在线| 欧美一级黄片免费视频| 欧美一区二区三区喷汁尤物| 欧美一区二区三区视频区| 国产欧美一区二区久久| 国产精品日韩精品一区| 91日韩在线观看你懂的| 免费特黄欧美亚洲黄片| 国产视频福利一区二区| 91插插插外国一区二区婷婷| 少妇丰满a一区二区三区| 欧美韩日在线观看一区| 台湾综合熟女一区二区| 国产精品偷拍视频一区| 日本免费一本一二区三区| 福利在线午夜绝顶三级| 日本一区不卡在线观看| 久久久精品区二区三区| 亚洲午夜av一区二区| 欧美日韩精品人妻二区三区| 中文字幕有码视频熟女| 能在线看的视频你懂的| 超碰在线播放国产精品| 亚洲欧美日本国产不卡| 亚洲人午夜精品射精日韩| 九九热精品视频免费在线播放| 亚洲内射人妻一区二区|