RT-Thread中堆和栈内存的分配
在嵌入式軟件開發中,我們經常會提到堆和棧,實際上堆和棧都是RAM上的物理內存空間,只是使用方式不同而已。棧和堆都是單片機RAM中一段連續的存儲空間,該段空間一般在啟動文件或鏈接腳本中指定,最后在C庫的_main函數中進行初始化。
STM32中的堆棧內存空間分配就在啟動文件中完成:
棧(STACK):由編譯器自動分配和釋放
堆(HEAP):有的地方也叫堆棧,一般由用戶自行分配和釋放,因此在分配好使用完成后要及時釋放內存,否則會導致系統可用的內存越來越少,我們管這種情況叫做內存泄漏
使用MDK5進行STM32項目開發時,當點擊全部編譯后會在Build Output窗口生成如下信息:
那么這些信息究竟是代表什么意思?
需要注意的是,啟動文件中定義的STACK和HEAP內存空間都是被包含到ZI-Data中的,如下圖片可對比說明:
當STACK空間大小設置為0x400時ZI-Data空間大小為2908Byte,設置為0x200時ZI-Data空間大小為2396Byte。2908-2396 = 512 = 0x200,因此可以說明棧空間是被包含在ZI-Data中的。將HEAP空間大小由0x200設置為0x100時,ZI-Data空間大小由2396變為2140,2396 - 2140 = 256,因此可以證明堆空間是被包含在ZI-Data中的。
在工程編譯完成后也會生成的對應.map文件,.map文件中詳細描述了各個函數在ROM中的存儲地址和大小,也可以看到程序中定義的全局變量、全局數組、常亮等在RAM中的存儲地址和大小,因此.map文件是非常重要的一個文件。在生成的.map的最后幾行,也可以看到如下信息:
那么這些信息究竟是代表什么意思?
Cortex-M3和Cortex-M4在設計之初就考慮到了對OS的高效支持,主要有3點:
在裸機系統中,所有的變量、函數調用、中斷處理等使用的棧空間均是MSP。在使用RTOS的多線程系統中,每個線程都是完全獨立互不干擾的,因此需要為每個線程分配獨立的棧空間,這個棧空間可以是預先分配好的全局數組(即存儲于RW-Data段或ZI-Data段),也可以是動態分配的一段內存空間(注意:這里動態分配的空間不是啟動文件中定義的堆空間,而是由RTOS管理的內存空間),但本質上它們都是RAM上的一段內存空間。
在RT-Thread中若需要動態內存管理,則需要先調用board.c--->rt_hw_board_init()--->rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);函數進行動態內存管理范圍的配置,即將HEAP_BEGIN和HEAP_END之間的內存空間作為動態內存空間交由RT-Thread進行管理。
在RT-Thread Master和RT-Thread Nano版本中,對于rt_system_heap_init()函數調用時填入的參數有些許差異,下面進行對比說明。
RT-Thread Master版本:
RT-Thread Master版本默認開啟RT-Thread中的動態內存分配,并將將HEAP_BEGIN和HEAP_END之間的內存空間作為動態內存空間交由RT-Thread進行管理。其中在board.h文件中有定義,具體如下:
Image$$RW_IRAM1$$ZI$$Limit是一個鏈接器導出的符號,代表ZI-Data段的結束,也即程序執行時所占RAM空間的結束地址,也是未使用RAM 空間的起始地址。因此RT-Thread Master版本中會默認將RAM中剩余的內存空間全部交給RT-Thread進行動態內存管理。
RT-Thread Nano版本:
RT-Thread Nano版本默認不開啟RT-Thread中的動態內存分配,因此凡是涉及到使用RT-Thread動態內存分配的函數都不能使用,如在創建線程時無法使用rt_thread_create()函數,只能使用rt_thread_init()函數等。用戶需開啟rtconfig.h中的RT_USING_HEAP宏開啟使用RT-Thread進行動態內存分配的功能,因為動態內存分配中有使用到信號量,因此還需打開RT_USING_SEMAPHORE宏。rt_system_heap_init()函數中使用的參數rt_heap_begin_get(), rt_heap_end_get()均在board.c文件中有定義,具體如下:
可以看到RT-Thread Nano版本中是將用戶定義好的大小為1024 * 4個字節的內存空間交由RT-Thread進行動態內存管理,而非將RAM中剩余的內存空間全部交給RT-Thread進行動態內存管理。
這里需要明白的是,程序中并不是將RAM空間用完的,假使RAM為20KByte,實際上只使用了8560Bytes,則剩余的RAM空間全部閑置在那兒。因此在RT-Thread Nano版本中使用數組作為動態內存堆時,可能會有一部分RAM空間是閑置的。
當把RT_HEAP_SIZE由1024 * 4Byte改為256 * 4Byte時,生成的工程信息中只有ZI-Data發生變化。由11124變為8052,即11124 - 8052 = 3072Byte,和程序中的變化值相等。因此,我們可以理解為:RT-Thread Nano是通過定義全局數組的方式在ZI-Data段占據了一段空間,并將這段空間交由RT-Thread進行動態內存分配。
如果不想使用RT-Thread-Nano中使用數組作為動態內存堆的方式,也可以使用和RT-Thread-Master中同樣的方法將ZI段結束作為動態內存堆起始地址,將RAM空間結束地址作為動態內存堆的結束地址。這樣可以將程序運行所需RAM空間之余的全部空間作為動態內存堆使用,即RAM上沒有閑置的內存空間。
將board.c中的代碼作如下修改:
其中#ifndef USE_ARRAY_AS_RTT_HEAP和#else之間的為新增加的代碼,當定義USE_ARRAY_AS_RTT_HEAP宏時使用用戶自定義數組作為動態內存堆,否則則使用ZI段結束到RAM空間結束之間的內存作為動態內存堆。
//#define USE_ARRAY_AS_RTT_HEAP //定義該宏后使用用戶自定義的數組作為RT-Thread的動態內存堆#ifndef USE_ARRAY_AS_RTT_HEAP#define STM32_SRAM1_START (0x20000000) #define STM32_SRAM1_END (STM32_SRAM1_START + 20 * 1024) // 結束地址 = 0x20000000(基址) + 20K(RAM大小)#if defined(__CC_ARM) || defined(__CLANG_ARM)extern int Image$$RW_IRAM1$$ZI$$Limit; // RW_IRAM1,需與鏈接腳本中運行時域名相對應#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文件如下所示,可以看到用戶自定義的數組消失了,程序使用棧頂地址0x20002b90和RAM空間結束地址0x20005000之間的RAM空間作為RT-Thread的動態內存堆。
當然,RT-Thread Master中也可以使用RT-Thread Nano中默認的使用用戶自定義的數組作為動態內存堆的方式。具體如何使用RAM空間,是很靈活的可以由用戶自行指定的。當你深入了解RAM空間的分配原理后(一定要研究.map文件,開發中很有用的一個文件),就會發現用戶就像是單片機的上帝,上帝可以任意支配手中的資源。而如何讓單片機中這些資源按照用戶的意愿去分配、去實現既定的功能是我們用戶需要去好好研究的。
總結
以上是生活随笔為你收集整理的RT-Thread中堆和栈内存的分配的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数字电路实验怎么接线视频讲解_铆钉是怎么
- 下一篇: button 隐藏属性_PyQt5实现仿