日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

RT-Thread中堆和栈内存的分配

發(fā)布時間:2023/12/10 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 RT-Thread中堆和栈内存的分配 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在嵌入式軟件開發(fā)中,我們經(jīng)常會提到堆和棧,實際上堆和棧都是RAM上的物理內(nèi)存空間,只是使用方式不同而已。棧和堆都是單片機(jī)RAM中一段連續(xù)的存儲空間,該段空間一般在啟動文件或鏈接腳本中指定,最后在C庫的_main函數(shù)中進(jìn)行初始化。

STM32中的堆棧內(nèi)存空間分配就在啟動文件中完成:

棧(STACK):由編譯器自動分配和釋放

堆(HEAP):有的地方也叫堆棧,一般由用戶自行分配和釋放,因此在分配好使用完成后要及時釋放內(nèi)存,否則會導(dǎo)致系統(tǒng)可用的內(nèi)存越來越少,我們管這種情況叫做內(nèi)存泄漏

使用MDK5進(jìn)行STM32項目開發(fā)時,當(dāng)點擊全部編譯后會在Build Output窗口生成如下信息:

那么這些信息究竟是代表什么意思?

  • Code:代碼段,存放程序的代碼部分
  • RO-Data:只讀數(shù)據(jù)段,存放程序中定義的常量
  • RW-Data:讀寫數(shù)據(jù)段,存放初始化為非0值得全局變量
  • ZI-Data:0數(shù)據(jù)段,存放初始化為0的全局變量或未初始化的全局變量(程序運行時會對未初始化的全局變量自動清0)
  • 需要注意的是,啟動文件中定義的STACK和HEAP內(nèi)存空間都是被包含到ZI-Data中的,如下圖片可對比說明:

    當(dāng)STACK空間大小設(shè)置為0x400時ZI-Data空間大小為2908Byte,設(shè)置為0x200時ZI-Data空間大小為2396Byte。2908-2396 = 512 = 0x200,因此可以說明棧空間是被包含在ZI-Data中的。將HEAP空間大小由0x200設(shè)置為0x100時,ZI-Data空間大小由2396變?yōu)?140,2396 - 2140 = 256,因此可以證明堆空間是被包含在ZI-Data中的。

    在工程編譯完成后也會生成的對應(yīng).map文件,.map文件中詳細(xì)描述了各個函數(shù)在ROM中的存儲地址和大小,也可以看到程序中定義的全局變量、全局?jǐn)?shù)組、常亮等在RAM中的存儲地址和大小,因此.map文件是非常重要的一個文件。在生成的.map的最后幾行,也可以看到如下信息:

    那么這些信息究竟是代表什么意思?

  • RO Size:程序在ROM中的存儲大小,包括Code段和RO-Data段
  • RW Size:程序運行時占用RAM空間的大小,包括RW-Data段和ZI-Data段
  • ROM Size:程序燒寫到ROM上時占用的空間大小,包括Code段和RO-Data段、RW-Data段(這里不包含ZI-Data段是因為ZI-Data段全部是0,將其存儲在ROM中毫無意義,因此只需記錄ZI-Data段占用的內(nèi)存空間,在程序運行時在RAM上開辟對應(yīng)大小的內(nèi)存空間并將該區(qū)域清0即可)
  • const int ci_max_len = 100; //RO段 static int si_loop_time = 300; //RW char *p1; //ZIint main(void) {int i = 0; //STACKchar c_say_a[] = "hello world"; //c_say_a存儲于STACK,"hello world"存儲于ROchar *p= &s[0]; //STACKstatic int si_i = 5; //RWchar *p2; //STACKp1 = (char *)malloc(100); //HEAPp2 = (int *)malloc(100); //HEAP...free(p1);free(p2);return 0; }

    Cortex-M3和Cortex-M4在設(shè)計之初就考慮到了對OS的高效支持,主要有3點:

  • 它們都具有一個內(nèi)置的的簡單的向下計數(shù)24位計數(shù)器SysTick。之所以要在處理器內(nèi)增加一個定時器,主要是為了提高軟件移植的方便性。所有基于Cortex-M3和Cortex-M4內(nèi)核的處理器具有相同的SysTick定時器,在一種Cortex-M3/M4處理器上實現(xiàn)的OS也能適用于其它Cortex-M3/M4處理器。因此,嵌入式OS的源碼可以很容易的移植到其它Cortex-M3/M4內(nèi)核的處理器上,無需為設(shè)備相關(guān)的定時器做修改。
  • Cortex-M3內(nèi)核支持雙堆棧機(jī)制,即主堆棧MSP和線程堆棧PSP。OS內(nèi)核和系統(tǒng)中的中斷或異常使用MSP,用戶創(chuàng)建的多線程任務(wù)使用PSP。
  • Cortex-M3/M4內(nèi)核支持特權(quán)模式和非特權(quán)模式,可以將用戶創(chuàng)建的多線程任務(wù)在非特權(quán)操作模式中運行,這樣可以限制其對一些寄存器的訪問,如NVIC、CONTROL寄存器等,以免因為不可靠的程序引起整個系統(tǒng)的崩潰。
  • 在裸機(jī)系統(tǒng)中,所有的變量、函數(shù)調(diào)用、中斷處理等使用的棧空間均是MSP。在使用RTOS的多線程系統(tǒng)中,每個線程都是完全獨立互不干擾的,因此需要為每個線程分配獨立的棧空間,這個棧空間可以是預(yù)先分配好的全局?jǐn)?shù)組(即存儲于RW-Data段或ZI-Data段),也可以是動態(tài)分配的一段內(nèi)存空間(注意:這里動態(tài)分配的空間不是啟動文件中定義的堆空間,而是由RTOS管理的內(nèi)存空間),但本質(zhì)上它們都是RAM上的一段內(nèi)存空間。

    在RT-Thread中若需要動態(tài)內(nèi)存管理,則需要先調(diào)用board.c--->rt_hw_board_init()--->rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);函數(shù)進(jìn)行動態(tài)內(nèi)存管理范圍的配置,即將HEAP_BEGIN和HEAP_END之間的內(nèi)存空間作為動態(tài)內(nèi)存空間交由RT-Thread進(jìn)行管理。

    在RT-Thread Master和RT-Thread Nano版本中,對于rt_system_heap_init()函數(shù)調(diào)用時填入的參數(shù)有些許差異,下面進(jìn)行對比說明。

    RT-Thread Master版本:

    RT-Thread Master版本默認(rèn)開啟RT-Thread中的動態(tài)內(nèi)存分配,并將將HEAP_BEGIN和HEAP_END之間的內(nèi)存空間作為動態(tài)內(nèi)存空間交由RT-Thread進(jìn)行管理。其中在board.h文件中有定義,具體如下:

    Image$$RW_IRAM1$$ZI$$Limit是一個鏈接器導(dǎo)出的符號,代表ZI-Data段的結(jié)束,也即程序執(zhí)行時所占RAM空間的結(jié)束地址,也是未使用RAM 空間的起始地址。因此RT-Thread Master版本中會默認(rèn)將RAM中剩余的內(nèi)存空間全部交給RT-Thread進(jìn)行動態(tài)內(nèi)存管理。

    RT-Thread Nano版本:

    RT-Thread Nano版本默認(rèn)不開啟RT-Thread中的動態(tài)內(nèi)存分配,因此凡是涉及到使用RT-Thread動態(tài)內(nèi)存分配的函數(shù)都不能使用,如在創(chuàng)建線程時無法使用rt_thread_create()函數(shù),只能使用rt_thread_init()函數(shù)等。用戶需開啟rtconfig.h中的RT_USING_HEAP宏開啟使用RT-Thread進(jìn)行動態(tài)內(nèi)存分配的功能,因為動態(tài)內(nèi)存分配中有使用到信號量,因此還需打開RT_USING_SEMAPHORE宏。rt_system_heap_init()函數(shù)中使用的參數(shù)rt_heap_begin_get(), rt_heap_end_get()均在board.c文件中有定義,具體如下:

    可以看到RT-Thread Nano版本中是將用戶定義好的大小為1024 * 4個字節(jié)的內(nèi)存空間交由RT-Thread進(jìn)行動態(tài)內(nèi)存管理,而非將RAM中剩余的內(nèi)存空間全部交給RT-Thread進(jìn)行動態(tài)內(nèi)存管理。

    這里需要明白的是,程序中并不是將RAM空間用完的,假使RAM為20KByte,實際上只使用了8560Bytes,則剩余的RAM空間全部閑置在那兒。因此在RT-Thread Nano版本中使用數(shù)組作為動態(tài)內(nèi)存堆時,可能會有一部分RAM空間是閑置的。

    當(dāng)把RT_HEAP_SIZE由1024 * 4Byte改為256 * 4Byte時,生成的工程信息中只有ZI-Data發(fā)生變化。由11124變?yōu)?052,即11124 - 8052 = 3072Byte,和程序中的變化值相等。因此,我們可以理解為:RT-Thread Nano是通過定義全局?jǐn)?shù)組的方式在ZI-Data段占據(jù)了一段空間,并將這段空間交由RT-Thread進(jìn)行動態(tài)內(nèi)存分配。

    如果不想使用RT-Thread-Nano中使用數(shù)組作為動態(tài)內(nèi)存堆的方式,也可以使用和RT-Thread-Master中同樣的方法將ZI段結(jié)束作為動態(tài)內(nèi)存堆起始地址,將RAM空間結(jié)束地址作為動態(tài)內(nèi)存堆的結(jié)束地址。這樣可以將程序運行所需RAM空間之余的全部空間作為動態(tài)內(nèi)存堆使用,即RAM上沒有閑置的內(nèi)存空間。

    將board.c中的代碼作如下修改:

    其中#ifndef USE_ARRAY_AS_RTT_HEAP和#else之間的為新增加的代碼,當(dāng)定義USE_ARRAY_AS_RTT_HEAP宏時使用用戶自定義數(shù)組作為動態(tài)內(nèi)存堆,否則則使用ZI段結(jié)束到RAM空間結(jié)束之間的內(nèi)存作為動態(tài)內(nèi)存堆。

    //#define USE_ARRAY_AS_RTT_HEAP //定義該宏后使用用戶自定義的數(shù)組作為RT-Thread的動態(tài)內(nèi)存堆#ifndef USE_ARRAY_AS_RTT_HEAP#define STM32_SRAM1_START (0x20000000) #define STM32_SRAM1_END (STM32_SRAM1_START + 20 * 1024) // 結(jié)束地址 = 0x20000000(基址) + 20K(RAM大小)#if defined(__CC_ARM) || defined(__CLANG_ARM)extern int Image$$RW_IRAM1$$ZI$$Limit; // RW_IRAM1,需與鏈接腳本中運行時域名相對應(yīng)#define HEAP_BEGIN ((void *)&Image$$RW_IRAM1$$ZI$$Limit)#endif#define HEAP_END STM32_SRAM1_END #else#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)#define RT_HEAP_SIZE 1024static uint32_t rt_heap[RT_HEAP_SIZE]; // heap default size: 4K(1024 * 4)RT_WEAK void *rt_heap_begin_get(void){return rt_heap;}RT_WEAK void *rt_heap_end_get(void){return rt_heap + RT_HEAP_SIZE;}#endif #endif/*** This function will initial your board.*/ void rt_hw_board_init() {/* this function is defined in main.c, so use extern indicate this is an external function */extern void SystemClock_Config(void); HAL_Init();SystemClock_Config(); /* System Clock Update */SystemCoreClockUpdate();/* System Tick Configuration */_SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);/* Call components board initial (use INIT_BOARD_EXPORT()) */ #ifdef RT_USING_COMPONENTS_INITrt_components_board_init(); #endif #define RT_USING_USER_MAIN #define RT_USING_HEAP #if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)#ifndef USE_ARRAY_AS_RTT_HEAPrt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);#elsert_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());#endif #endif }

    編譯后生成的.map文件如下所示,可以看到用戶自定義的數(shù)組消失了,程序使用棧頂?shù)刂?x20002b90和RAM空間結(jié)束地址0x20005000之間的RAM空間作為RT-Thread的動態(tài)內(nèi)存堆。

    當(dāng)然,RT-Thread Master中也可以使用RT-Thread Nano中默認(rèn)的使用用戶自定義的數(shù)組作為動態(tài)內(nèi)存堆的方式。具體如何使用RAM空間,是很靈活的可以由用戶自行指定的。當(dāng)你深入了解RAM空間的分配原理后(一定要研究.map文件,開發(fā)中很有用的一個文件),就會發(fā)現(xiàn)用戶就像是單片機(jī)的上帝,上帝可以任意支配手中的資源。而如何讓單片機(jī)中這些資源按照用戶的意愿去分配、去實現(xiàn)既定的功能是我們用戶需要去好好研究的。

    總結(jié)

    以上是生活随笔為你收集整理的RT-Thread中堆和栈内存的分配的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。