FreeRTOS内存管理
生活随笔
收集整理的這篇文章主要介紹了
FreeRTOS内存管理
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
堆空間是一個(gè)數(shù)組,configTOTAL_HEAP_SIZE表示堆空間大小,在FreeRTOSConfig.h中宏定義
/* 由應(yīng)用程序創(chuàng)建堆區(qū),大小為configTOTAL_HEAP_SIZE */ #if (configAPPLICATION_ALLOCATED_HEAP == 1)extern uint8_t ucHeap[configTOTAL_HEAP_SIZE]; /* 由內(nèi)核創(chuàng)建堆區(qū),大小為configTOTAL_HEAP_SIZE */ #elsestatic uint8_t ucHeap[configTOTAL_HEAP_SIZE]; #endif?
?
將每一個(gè)空閑的內(nèi)存塊組織成一個(gè)結(jié)構(gòu)體,并將所有空閑塊結(jié)構(gòu)體掛接到一個(gè)鏈表上
/* 初始化堆區(qū) */ static void prvHeapInit(void) {BlockLink_t *pxFirstFreeBlock;uint8_t *pucAlignedHeap;size_t uxAddress;/* 堆區(qū)總大小 */size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;/* 堆區(qū)首地址 */uxAddress = (size_t)ucHeap;/* 如果堆區(qū)首地址沒有8字節(jié)對(duì)齊 */if((uxAddress & portBYTE_ALIGNMENT_MASK) != 0){/* 對(duì)堆區(qū)首地址進(jìn)行8字節(jié)對(duì)齊 */uxAddress += (portBYTE_ALIGNMENT - 1);uxAddress &= ~((size_t)portBYTE_ALIGNMENT_MASK);/* 更新堆區(qū)總大小 */xTotalHeapSize -= uxAddress - (size_t)ucHeap;}/* 8字節(jié)對(duì)齊后的堆區(qū)首地址 */pucAlignedHeap = (uint8_t *)uxAddress;/* 將首節(jié)點(diǎn)next指針指向堆區(qū)首地址(沒分配前堆區(qū)是一整個(gè)內(nèi)存塊) */xStart.pxNextFreeBlock = (void *)pucAlignedHeap;/* 首節(jié)點(diǎn)只起到頭部作用,不表示內(nèi)存塊,因此大小設(shè)為0 */xStart.xBlockSize = (size_t)0;/* 尾節(jié)點(diǎn)被組織成一個(gè)大小為0的內(nèi)存塊,放在整個(gè)堆區(qū)的末端 */uxAddress = ((size_t)pucAlignedHeap) + xTotalHeapSize;/* 尾節(jié)點(diǎn)共占用一個(gè)結(jié)構(gòu)體大小,計(jì)算出尾節(jié)點(diǎn)首地址 */uxAddress -= xHeapStructSize;/* 將尾節(jié)點(diǎn)首地址進(jìn)行8字節(jié)對(duì)齊 */uxAddress &= ~((size_t)portBYTE_ALIGNMENT_MASK);/* 將尾節(jié)點(diǎn)調(diào)整后的首地址賦值給pxEnd */pxEnd = (void *)uxAddress;/* 尾節(jié)點(diǎn)內(nèi)存塊大小為0 */pxEnd->xBlockSize = 0;/* 尾節(jié)點(diǎn)next指針指向NULL */pxEnd->pxNextFreeBlock = NULL;/* 初始化時(shí)要先將整個(gè)堆區(qū)組織成第一個(gè)內(nèi)存塊 */pxFirstFreeBlock = (void *)pucAlignedHeap;/* 大小為去掉尾節(jié)點(diǎn)后的堆區(qū)大小 */pxFirstFreeBlock->xBlockSize = uxAddress - (size_t)pxFirstFreeBlock;/* 沒分配前第一個(gè)內(nèi)存塊指向尾節(jié)點(diǎn) */pxFirstFreeBlock->pxNextFreeBlock = pxEnd;/* 初始化時(shí)堆區(qū)剩余最小空間時(shí)的剩余空閑空間大小為第一個(gè)內(nèi)存塊大小 */xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;/* 初始化時(shí)堆區(qū)剩余空間總大小為第一個(gè)內(nèi)存塊大小 */xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;/* 計(jì)算出內(nèi)存塊是否已分配標(biāo)志位在內(nèi)存塊結(jié)構(gòu)體中xBlockSize中的位置(最高位) */xBlockAllocatedBit = ((size_t)1) << ((sizeof(size_t) * heapBITS_PER_BYTE) - 1); }堆內(nèi)存初始化完成后,組織形式如下圖:
?
?
找到第一個(gè)滿足大小的空閑內(nèi)存塊,進(jìn)行內(nèi)存分配。需要注意的是如果內(nèi)存塊遠(yuǎn)遠(yuǎn)大于請(qǐng)求分配內(nèi)存的大小,還需要將該空閑內(nèi)存塊的剩余部分重新組織,并插入到鏈表中。
/* 功能:請(qǐng)求動(dòng)態(tài)分配內(nèi)存xWantedSize:請(qǐng)求分配的內(nèi)存大小返回值:已分配的內(nèi)存地址,NULL表示分配失敗 */ void *pvPortMalloc(size_t xWantedSize) {BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;void *pvReturn = NULL;/* 臨界區(qū) */vTaskSuspendAll();{/* 首次分配內(nèi)存的時(shí)候堆區(qū)未初始化,pxEnd指向NULL,需要進(jìn)行初始化 */if(pxEnd == NULL){/* 初始化堆區(qū) */prvHeapInit();}else{mtCOVERAGE_TEST_MARKER();}/* 請(qǐng)求分配的內(nèi)存大小最高位不能為1,因?yàn)樽罡呶槐挥脕肀硎驹搩?nèi)存塊是否已分配 *//* 換言之不能申請(qǐng)超過2G字節(jié) */if((xWantedSize & xBlockAllocatedBit) == 0){/* 請(qǐng)求分配的內(nèi)存大小必須大于0 */if(xWantedSize > 0){/* 要對(duì)內(nèi)存進(jìn)行管理,必須加上內(nèi)存塊結(jié)構(gòu)體 */xWantedSize += xHeapStructSize;/* 配的字節(jié)(加上結(jié)構(gòu)體后)是否已經(jīng)字節(jié)對(duì)齊,如果沒有則要進(jìn)行對(duì)齊 */if((xWantedSize & portBYTE_ALIGNMENT_MASK) != 0x00){/* 進(jìn)行字節(jié)對(duì)齊 */xWantedSize += (portBYTE_ALIGNMENT - (xWantedSize & portBYTE_ALIGNMENT_MASK));/* 如果對(duì)齊不了,說明出現(xiàn)錯(cuò)誤,進(jìn)行斷言 */configASSERT((xWantedSize & portBYTE_ALIGNMENT_MASK) == 0);}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}/* 請(qǐng)求分配的大小必須大于0,并且請(qǐng)求分配的大小(加上結(jié)構(gòu)體后)必須剩余空閑內(nèi)存總大小 */if((xWantedSize > 0) && (xWantedSize <= xFreeBytesRemaining)){/* 遍歷所有可分配的內(nèi)存塊,檢查是否存在大小合適的內(nèi)存塊 */pxPreviousBlock = &xStart;pxBlock = xStart.pxNextFreeBlock;while((pxBlock->xBlockSize < xWantedSize) && (pxBlock->pxNextFreeBlock != NULL)){pxPreviousBlock = pxBlock;pxBlock = pxBlock->pxNextFreeBlock;}/* 如果存在,則進(jìn)行分配 */if(pxBlock != pxEnd){/* 分配成功返回的內(nèi)存地址 */pvReturn = (void *)(((uint8_t *)pxPreviousBlock->pxNextFreeBlock) + xHeapStructSize);/* 將該內(nèi)存塊從空閑內(nèi)存塊鏈表中移除 */pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;/* 如果該內(nèi)存塊太大,去掉請(qǐng)求的大小(加上結(jié)構(gòu)體后)還夠分配一個(gè)內(nèi)存塊的話,需要將多余的內(nèi)存組織成新的內(nèi)存塊,并插入空閑內(nèi)存塊鏈表 */if((pxBlock->xBlockSize - xWantedSize) > heapMINIMUM_BLOCK_SIZE){/* 新的內(nèi)存塊在剩余內(nèi)存地址處 */pxNewBlockLink = (void *)(((uint8_t *)pxBlock) + xWantedSize);/* 如果新的內(nèi)存塊不是8字節(jié)對(duì)齊的話,進(jìn)行斷言 */configASSERT((((size_t)pxNewBlockLink) & portBYTE_ALIGNMENT_MASK) == 0);/* 新的內(nèi)存塊大小為該內(nèi)存塊減去請(qǐng)求大小(加上結(jié)構(gòu)體后) */pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;/* 該內(nèi)存塊大小更新為請(qǐng)求大小(加上結(jié)構(gòu)體) */pxBlock->xBlockSize = xWantedSize;/* 將新內(nèi)存塊插入空閑內(nèi)存塊鏈表(內(nèi)存塊地址從小到大排列) */prvInsertBlockIntoFreeList(pxNewBlockLink);}else{mtCOVERAGE_TEST_MARKER();}/* 更新剩余空閑內(nèi)存總大小 */xFreeBytesRemaining -= pxBlock->xBlockSize;/* 更新堆區(qū)剩余最小空間時(shí)的剩余空閑空間大小 */if(xFreeBytesRemaining < xMinimumEverFreeBytesRemaining){xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;}else{mtCOVERAGE_TEST_MARKER();}/* 將該內(nèi)存塊最高位置1,表示該內(nèi)存塊已經(jīng)被分配 */pxBlock->xBlockSize |= xBlockAllocatedBit;/* 將該內(nèi)存塊next指針指向NULL */pxBlock->pxNextFreeBlock = NULL;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}/* 設(shè)置一個(gè)跟蹤點(diǎn),輸出內(nèi)存地址和大小 */traceMALLOC(pvReturn, xWantedSize);}/* 退出臨界區(qū) */(void)xTaskResumeAll();/* 如果啟用內(nèi)存動(dòng)態(tài)分配失敗鉤子函數(shù),則要判斷是否失敗并調(diào)用鉤子函數(shù) */#if (configUSE_MALLOC_FAILED_HOOK == 1){/* 判斷是否分配失敗,如果失敗則要調(diào)用鉤子函數(shù) */if(pvReturn == NULL){extern void vApplicationMallocFailedHook(void);/* 調(diào)用鉤子函數(shù) */vApplicationMallocFailedHook();}else{mtCOVERAGE_TEST_MARKER();}}#endif/* 如果分配空間指針不是8字節(jié)對(duì)齊,則進(jìn)行斷言 */configASSERT((((size_t)pvReturn) & (size_t)portBYTE_ALIGNMENT_MASK) == 0);return pvReturn; }釋放內(nèi)存的時(shí)候,就是將該段內(nèi)存重新組織成空閑內(nèi)存塊結(jié)構(gòu)體,并重新插入鏈表
/* 功能:釋放動(dòng)態(tài)分配的內(nèi)存pv:請(qǐng)求釋放的內(nèi)存地址 */ void vPortFree(void *pv) {uint8_t *puc = (uint8_t *)pv;BlockLink_t *pxLink;/* 請(qǐng)求釋放的地址不能為空 */if(pv != NULL){/* 計(jì)算出該內(nèi)存所在內(nèi)存塊(結(jié)構(gòu)體 + 內(nèi)存)的首地址 */puc -= xHeapStructSize;pxLink = (void *)puc;/* 如果該節(jié)點(diǎn)并沒有已經(jīng)被分配,則進(jìn)行斷言 */configASSERT((pxLink->xBlockSize & xBlockAllocatedBit) != 0);/* 如果該節(jié)點(diǎn)next指針沒有指向NULL,則進(jìn)行斷言 */configASSERT(pxLink->pxNextFreeBlock == NULL);/* 該內(nèi)存塊必須已經(jīng)被分配 */if((pxLink->xBlockSize & xBlockAllocatedBit) != 0){/* 該內(nèi)存塊next指針必須指向NULL */if(pxLink->pxNextFreeBlock == NULL){/* 將該內(nèi)存塊最高位置0,表示該內(nèi)存塊未被分配 */pxLink->xBlockSize &= ~xBlockAllocatedBit;/* 進(jìn)入臨界區(qū) */vTaskSuspendAll();{/* 更新剩余內(nèi)存塊大小 */xFreeBytesRemaining += pxLink->xBlockSize;/* 設(shè)置一個(gè)跟蹤點(diǎn),輸出內(nèi)存地址和大小 */traceFREE(pv, pxLink->xBlockSize);/* 將新內(nèi)存塊插入空閑內(nèi)存塊鏈表(內(nèi)存塊地址從小到大排列) */prvInsertBlockIntoFreeList(((BlockLink_t *)pxLink));}/* 退出臨界區(qū) */(void)xTaskResumeAll();}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}} }經(jīng)過幾次分配和釋放之后,內(nèi)存空間排列變得很復(fù)雜,如下圖:
?
?
為了防止內(nèi)存碎片化,在空閑塊重新插入鏈表的時(shí)候會(huì)將地址相鄰的兩塊內(nèi)存空間進(jìn)行合并
/* 將新內(nèi)存塊插入空閑內(nèi)存塊鏈表(內(nèi)存塊地址從小到大排列) */ static void prvInsertBlockIntoFreeList(BlockLink_t *pxBlockToInsert) {BlockLink_t *pxIterator;uint8_t *puc;/* 內(nèi)存地址從小到大進(jìn)行排列,尋找合適的插入點(diǎn) */for(pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock){}/* 如果和前一個(gè)內(nèi)存塊地址相鄰,則要進(jìn)行合并 */puc = (uint8_t *)pxIterator;if((puc + pxIterator->xBlockSize) == (uint8_t *)pxBlockToInsert){pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;pxBlockToInsert = pxIterator;}else{mtCOVERAGE_TEST_MARKER();}/* 如果和后一個(gè)內(nèi)存塊地址相鄰并且后一個(gè)內(nèi)存塊不是尾節(jié)點(diǎn),則要進(jìn)行合并 */puc = (uint8_t * )pxBlockToInsert;if((puc + pxBlockToInsert->xBlockSize) == (uint8_t *)pxIterator->pxNextFreeBlock){/* 如果不是尾節(jié)點(diǎn),進(jìn)行合并 */if(pxIterator->pxNextFreeBlock != pxEnd){pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;}/* 如果是尾節(jié)點(diǎn),則直接指向尾節(jié)點(diǎn) */else{pxBlockToInsert->pxNextFreeBlock = pxEnd;}}/* 如果和后一個(gè)內(nèi)存塊地址不相鄰,則直接指向下一個(gè)內(nèi)存塊 */else{pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;}/* 將該內(nèi)存塊插入空閑內(nèi)存塊鏈表 */if(pxIterator != pxBlockToInsert){pxIterator->pxNextFreeBlock = pxBlockToInsert;}else{mtCOVERAGE_TEST_MARKER();} }將原先分配的空間2釋放之后,內(nèi)存的組織形式,如下圖:
?
?
內(nèi)存管理還提供了一些其他的接口
/* 功能:獲取堆區(qū)剩余內(nèi)存大小返回值:剩余內(nèi)存大小 */ size_t xPortGetFreeHeapSize(void) {return xFreeBytesRemaining; } /* 功能:獲取堆區(qū)剩余最小空間時(shí)的剩余空閑空間大小返回值:堆區(qū)剩余最小空間時(shí)的剩余空閑空間大小 */ size_t xPortGetMinimumEverFreeHeapSize(void) {return xMinimumEverFreeBytesRemaining; }?
總結(jié)
以上是生活随笔為你收集整理的FreeRTOS内存管理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: STM32之GPIO原理
- 下一篇: Simulink之器件换流式电压型无源逆