久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 3498|回復: 0
收起左側

STM32 大小端模式 與 堆棧及其增長方向分析

[復制鏈接]
ID:105323 發表于 2016-2-12 03:22 | 顯示全部樓層 |閱讀模式
       首先感謝這位網友,寫出這篇實用的深度好文,這篇文章是他的原創。復制過來,可以時?纯础1救藗人認為,能看懂這篇文章的人才有資格說懂一點計算機技術!當然,本文優化整理后就更出彩!堆heap和棧stack都是計算機內存,只是屬性不同而已,stack由編譯器自動分配和回收,heap由程序員手動分配和回收,通過malloc和free函數。




棧增長和大端/小端問題是和CPU相關的兩個問題.

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

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

Stack_Size      EQU     0x00000800

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

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




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



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

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

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

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


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

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

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



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

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

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

CPU到底是大端還是小端,可以通過如下代碼測試:
//CPU大小端
//0,小端模式;1,大端模式.
static u8 cpu_endian;

//獲取CPU大小端模式,結果保存在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內存的問題.
    還是以附件工程為例,在前面第一個圖,程序總共占用內存:20+2348字節,這么多內存,到底是怎么得來的呢?
我們可以雙擊Project側邊欄的:Targt1,會彈出test.map,在這個里面,我們就可以清楚的知道這些內存到底是怎么來的了.在這個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字節
       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字節.
       384         24          0          8        200       3050   usart.o//usart.c定義了一個串口接收數組buffer,占用200字節.
    ----------------------------------------------------------------------
      1800        278        336         20       2248     216735   Object Totals //總共2248+20字節
         0          0         32          0          0          0   (incl. Generated)
         0          0          0          2          0          0   (incl. Padding)//2字節用于對其
    ----------------------------------------------------------------------
      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字節
        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  //調用的庫用了100字節.
        10          0          2          0          4          0   (incl. Padding)   //用于對其多占用了4個字節
    ----------------------------------------------------------------------
      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)
==============================================================================

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

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

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

//獲取CPU大小端模式,結果保存在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); //系統時鐘設置
 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;  
 }  
}
測試結果如圖:



附加:

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

原子哥,這里沒有看明白,我理解的是不是應該這樣 
地址              |            HEX         | 
0X2000 0010          78      
0X2000 0011          56 
0X2000 0012          34 
0X2000 0013          12

 
另外,經過測試,確實是這樣。
STM32的內存分配,應該分為兩種情況。
1,使用了系統的malloc。
2,未使用系統的malloc。

第一種情況(使用malloc):
STM32的內存分配規律:
從0X20000000開始依次為:靜態存儲區+堆區+棧區

第二種情況(不使用malloc):
STM32的內存分配規律:
從0X20000000開始依次為:靜態存儲區+棧區

第二種情況不存在堆區。
所以,一般對于我們開發板例程,實際上,沒有所謂堆區的概念,而僅僅是:靜態存儲區+棧區。
無論哪種情況,所有的全局變量,包括靜態變量之類的,全部存儲在靜態存儲區。
緊跟靜態存儲區之后的,是堆區(如沒用到malloc,則沒有該區),之后是棧區。
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 男人电影天堂 | 国产精品国产成人国产三级 | www.亚洲一区二区 | 亚洲精品中文字幕中文字幕 | 国产精品免费高清 | 欧美三区 | 性xxxxx| 精品一区二区三区91 | 亚洲国产成人在线观看 | 免费精品 | 久久国产一区二区三区 | 久久久久国色av免费观看性色 | 超碰免费观看 | 欧美成人hd | 久久国内精品 | 一区二区三区精品视频 | 成人精品在线观看 | 二区三区在线观看 | 久久精品久久久久久 | 中文字幕高清视频 | 超碰97人人人人人蜜桃 | 91在线观看视频 | 五月婷婷婷 | 亚洲精品高清视频在线观看 | 欧美一级片免费看 | 噜久寡妇噜噜久久寡妇 | 亚洲精品免费观看 | 91av大全| 久久青视频 | aaaaaa大片免费看最大的 | 天天操天天干天天爽 | 99久久精品国产毛片 | 成人精品免费视频 | 亚洲国产电影 | 亚洲一区二区免费电影 | 亚洲精品视频免费观看 | 国产成人99久久亚洲综合精品 | 精品久久99| 国产精品96久久久久久 | 玖玖国产精品视频 | 亚洲一区二区三区免费观看 |