FreeRTOS 之五 动态内存管理(heap_1.c)详解
寫在前面
??寫這篇文章時(shí),網(wǎng)上已經(jīng)有了鋪天蓋地的文章來(lái)介紹 FreeRTOS 的動(dòng)態(tài)內(nèi)存。之所以還去寫這篇博文,主要還是記錄自己的學(xué)習(xí)過(guò)程。結(jié)合源代碼一步一步分析一下FreeRTOS究竟是怎么實(shí)現(xiàn)的其內(nèi)存部分。
簡(jiǎn)介
??FreeRTOS 支持 5 種動(dòng)態(tài)內(nèi)存管理方案,分別通過(guò)文件 heap_1.c,heap_2.c,heap_3.c,heap_4.c 和 heap_5.c 實(shí)現(xiàn)。這 5 個(gè)文件在 FreeRTOS 源碼包中的路徑是:FreeRTOS\Source\portable\MemMang。具體如下圖:
用戶在自己的項(xiàng)目中如果要使用 FreeRTOS ,則必須從中以上5中內(nèi)存方案中選擇一種 。5種方案各有各的優(yōu)勢(shì),分別適用于不同的應(yīng)用場(chǎng)景。
??再具體的實(shí)現(xiàn)上,FreeRTOS 內(nèi)核規(guī)定的幾個(gè)內(nèi)存管理函數(shù)原型。系統(tǒng)內(nèi)部及用戶如果要使用內(nèi)存,只能通過(guò)該函數(shù)接口進(jìn)行申請(qǐng)。因此完全可以有用戶自己實(shí)現(xiàn)。具體函數(shù)接口如下(不同方案稍有區(qū)別):
- void *pvPortMalloc( size_t xSize ) :內(nèi)存申請(qǐng)函數(shù)
- void vPortFree( void *pv ) :內(nèi)存釋放函數(shù)
- void vPortInitialiseBlocks( void ) :初始化內(nèi)存堆函數(shù)
- size_t xPortGetFreeHeapSize( void ):獲取當(dāng)前未分配的內(nèi)存堆大小
- size_t xPortGetMinimumEverFreeHeapSize( void ):獲取未分配的內(nèi)存堆歷史最小值
heap_1.c
??這種內(nèi)存分配方式最簡(jiǎn)單直接,速度快程序簡(jiǎn)單。適用于分配完內(nèi)存后不需要回收的場(chǎng)合。只允許管理一個(gè)靜態(tài)的數(shù)組ucHeap,內(nèi)存從靜態(tài)Ram中由系統(tǒng)分配,不能指定管理外部SRAM,或者管理堆中的內(nèi)存。下面就結(jié)合源碼,詳細(xì)來(lái)介紹一下heap_1.c。
配置
??在 FreeRTOS 的配置文件(FreeRTOSConfig.h)中,關(guān)于內(nèi)存管理部分的配置項(xiàng)主要有以下幾個(gè),要使用FreeRTOS 提供的內(nèi)存實(shí)現(xiàn)策略,則必須進(jìn)行有效的配置:
- configSUPPORT_STATIC_ALLOCATION :
設(shè)置為1,那么可以使用應(yīng)用程序編寫器提供的RAM創(chuàng)建RTOS對(duì)象。設(shè)置為0,則只能使用從FreeRTOS堆分配的RAM創(chuàng)建RTOS對(duì)象。默認(rèn)(未定義時(shí))為0 - configSUPPORT_DYNAMIC_ALLOCATION :
設(shè)置為1,則可以使用從FreeRTOS堆中自動(dòng)分配的RAM創(chuàng)建RTOS對(duì)象。設(shè)置為0,則只能使用應(yīng)用程序編寫器提供的RAM創(chuàng)建RTOS對(duì)象。默認(rèn)(未定義)為1 - configTOTAL_HEAP_SIZE :
FreeRTOS堆中可用的RAM總量。該值僅在configSUPPORT_DYNAMIC_ALLOCATION設(shè)置為 1 且應(yīng)用程序使用FreeRTOS源代碼下載中提供的示例內(nèi)存分配方案之一時(shí)該定義才有效。 有關(guān)詳細(xì)信息,請(qǐng)參閱內(nèi)存配置部分。 - configAPPLICATION_ALLOCATED_HEAP :
默認(rèn)情況下,FreeRTOS堆由FreeRTOS聲明,并由鏈接器放置在內(nèi)存中。 將configAPPLICATION_ALLOCATED_HEAP設(shè)置為1允許應(yīng)用程序編寫器聲明堆,這允許應(yīng)用程序?qū)⒍逊胖迷趦?nèi)存中的任何位置。
如果使用heap_1.c,heap_2.c或heap_4.c,并且configAPPLICATION_ALLOCATED_HEAP設(shè)置為1,那么應(yīng)用程序編寫器必須提供一個(gè)具有完全名稱和維度的uint8_t數(shù)組,如下所示:uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
該數(shù)組將用作FreeRTOS堆。 如何將數(shù)組放置在特定的內(nèi)存位置取決于使用的編譯器 - 請(qǐng)參閱您的編譯器文檔。
??從上面的配置不難看出,FreeRTOS給與了用戶極大的權(quán)限(配置靈活性),甚至允許完全由用戶自己實(shí)現(xiàn)內(nèi)存堆的管理。例如,使用使用靜態(tài)分配方式(版本9.0.0之后)。
??heap_1.c是這是 5 種內(nèi)存管理方案中最簡(jiǎn)單的一個(gè),簡(jiǎn)單到只能申請(qǐng)內(nèi)存(沒有釋放)。heap_1.c方案中,內(nèi)存堆實(shí)際上是一個(gè)很大的數(shù)組,名為:static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];。源碼如下:
具體的說(shuō)明,見上面的注釋即可。
內(nèi)存對(duì)齊
??在 portmacro.h (Source/Portable/ + 對(duì)應(yīng)編譯器 + 平臺(tái) 目錄下) 的常量 portBYTE_ALIGNMENT 定義了字節(jié)對(duì)齊,對(duì)應(yīng)的這個(gè)變量決定了portable.h 中的一個(gè)常量 portBYTE_ALIGNMENT_MASK, 對(duì)應(yīng)關(guān)系如下:
| 32(表示以32個(gè)字節(jié)對(duì)齊) | 0x001f |
| 16(表示以16個(gè)字節(jié)對(duì)齊) | 0x000f |
| 8(表示以8個(gè)字節(jié)對(duì)齊) | 0x0007 |
| 4(表示以4個(gè)字節(jié)對(duì)齊) | 0x0003 |
| 2(表示以2個(gè)字節(jié)對(duì)齊) | 0x0001 |
| 1(表示以1個(gè)字節(jié)對(duì)齊) | 0x0000 |
至于為什么會(huì)考慮內(nèi)存對(duì)齊呢?這個(gè)在LwIP的博文中有詳細(xì)的說(shuō)明,再次不在贅述!
void *pvPortMalloc( size_t xWantedSize )
這個(gè)函數(shù)是由 FreeRTOS 規(guī)定的內(nèi)存分配函數(shù)。系統(tǒng)內(nèi)部及用戶如果要使用內(nèi)存,只能通過(guò)該函數(shù)接口進(jìn)行申請(qǐng)。
void *pvPortMalloc( size_t xWantedSize ) { void *pvReturn = NULL; /* 按 portBYTE_ALIGNMENT 對(duì)齊后內(nèi)存首地址。!!!注意這是個(gè)靜態(tài)變量,在第一次使用時(shí)會(huì)被初始化!!! */ static uint8_t *pucAlignedHeap = NULL; /* 確保指定的靜態(tài)內(nèi)存塊是 按 portBYTE_ALIGNMENT 對(duì)齊的(如果指定的對(duì)齊為1字節(jié),就沒必要處理了). */#if( portBYTE_ALIGNMENT != 1 ){if( xWantedSize & portBYTE_ALIGNMENT_MASK ) /* 其實(shí)就是xWantedSize % portBYTE_ALIGNMENT */{/* Byte alignment required.將 xWantedSize 按照 portBYTE_ALIGNMENT 強(qiáng)制對(duì)齊 */xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );}}#endifvTaskSuspendAll(); /* 掛起所有任務(wù),防止重入 */{if( pucAlignedHeap == NULL ) /* 靜態(tài)變量,僅在第一次初始化 */{/* Ensure the heap starts on a correctly aligned boundary. 這個(gè)實(shí)現(xiàn)對(duì)齊的方式可以參考,很溜! */pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );}/* Check there is enough room left for the allocation. 檢查剩余空間是否足夠,且 沒有溢出 */if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) &&( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) )/* Check for overflow. */{/* Return the next free byte then increment the index past thisblock. */pvReturn = pucAlignedHeap + xNextFreeByte;xNextFreeByte += xWantedSize;}traceMALLOC( pvReturn, xWantedSize );}( void ) xTaskResumeAll(); /* 恢復(fù)任務(wù) */#if( configUSE_MALLOC_FAILED_HOOK == 1 ){/* 如果分配內(nèi)存失敗,調(diào)用回調(diào)函數(shù)(如果開啟了鉤子函數(shù)) */if( pvReturn == NULL ){extern void vApplicationMallocFailedHook( void );vApplicationMallocFailedHook();}}#endifreturn pvReturn; }void vPortFree( void *pv )
??這個(gè)函數(shù)是由 FreeRTOS 規(guī)定的內(nèi)存釋放函數(shù)。系統(tǒng)內(nèi)部及用戶如果要釋放內(nèi)存,只能通過(guò)該函數(shù)接口進(jìn)行申請(qǐng)。有下面的實(shí)現(xiàn)可知,在heap_1.c方案,內(nèi)存一旦申請(qǐng)便無(wú)法釋放!
void vPortFree( void *pv ) {/* Memory cannot be freed using this scheme. See heap_2.c, heap_3.c andheap_4.c for alternative implementations, and the memory management pages ofhttp://www.FreeRTOS.org for more information. */( void ) pv;/* Force an assert as it is invalid to call this function. */configASSERT( pv == NULL ); }void vPortInitialiseBlocks( void )
??這個(gè)函數(shù)是由 FreeRTOS 規(guī)定的內(nèi)存管理初始化函數(shù)。
void vPortInitialiseBlocks( void ) {/* Only required when static memory is not cleared. */xNextFreeByte = ( size_t ) 0; }size_t xPortGetFreeHeapSize( void )
??這個(gè)函數(shù)是由 FreeRTOS 規(guī)定的獲取動(dòng)態(tài)內(nèi)存的剩余大小的函數(shù)。唯一需要注意的就是,其大小是在對(duì)齊之后的!
size_t xPortGetFreeHeapSize( void ) {return ( configADJUSTED_HEAP_SIZE - xNextFreeByte ); }總結(jié)
以上是生活随笔為你收集整理的FreeRTOS 之五 动态内存管理(heap_1.c)详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 常用算法 之三 详解 SHA1 实现(基
- 下一篇: Python 之 新手安装详解 、安装目