FreeRTOS動態(tài)內(nèi)存分配管理heap_2示例
heap_2.c
內(nèi)存堆管理
heap_2和heap_1一樣是開辟一個大數(shù)組作為堆空間供用戶使用,但是采用單項不循環(huán)鏈表來管理內(nèi)存的分配釋放,主要思想是用鏈表把內(nèi)存塊串起來,數(shù)據(jù)結構如下
/* Define the linked list structure. This is used to link free blocks in order
of their size. */
typedef struct A_BLOCK_LINK
{
//指向下一個空閑內(nèi)存塊管理結構體
struct A_BLOCK_LINK *pxNextFreeBlock; /*<< The next free block in the list. */
//記錄申請的字節(jié)數(shù),包括鏈表占用所占字節(jié)數(shù)
size_t xBlockSize; /*<< The size of the free block. */
} BlockLink_t;
與引入鏈表管理而帶來的相關變量如下
//鏈表結構體對齊后所占字節(jié)數(shù) static const uint16_t heapSTRUCT_SIZE = ( ( sizeof ( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK ); //2倍鏈表結構體對齊后所占字節(jié)數(shù),這作為一個閾值,在分配時起作用 #define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( heapSTRUCT_SIZE * 2 ) ) /* Create a couple of list links to mark the start and end of the list. */ //定義2個局部靜態(tài)全局結構體變量用于管理 static BlockLink_t xStart, xEnd;
還剩空閑字節(jié)數(shù)
/* Keeps track of the number of free bytes remaining, but says nothing about fragmentation. */ static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE;
分配
void *pvPortMalloc( size_t xWantedSize )
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
static BaseType_t xHeapHasBeenInitialised = pdFALSE;
void *pvReturn = NULL;
//掛起調(diào)度器,防止函數(shù)重入
vTaskSuspendAll();
{
/* If this is the first call to malloc then the heap will require
initialisation to setup the list of free blocks. */
//第一次調(diào)用會初始化內(nèi)存堆
if( xHeapHasBeenInitialised == pdFALSE )
{
prvHeapInit();
xHeapHasBeenInitialised = pdTRUE;
}
/* The wanted size is increased so it can contain a BlockLink_t
structure in addition to the requested amount of bytes. */
if( xWantedSize > 0 )
{
//用戶分配字節(jié)數(shù)+管理結構體占用字節(jié)數(shù)
xWantedSize += heapSTRUCT_SIZE;
/* Ensure that blocks are always aligned to the required number of bytes. */
//總的字節(jié)數(shù)再做此字節(jié)對齊
if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0 )
{
/* Byte alignment required. */
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
}
//待分配字節(jié)數(shù)大于0且小于總共堆字節(jié)數(shù)
if( ( xWantedSize > 0 ) && ( xWantedSize < configADJUSTED_HEAP_SIZE ) )
{
/* Blocks are stored in byte order - traverse the list from the start
(smallest) block until one of adequate size is found. */
//pxPreviousBlock指向頭鏈表
pxPreviousBlock = &xStart;
//pxBlock指向第一個開始空閑塊
pxBlock = xStart.pxNextFreeBlock;
//當pxBlock所管理的空閑塊字節(jié)數(shù)小于待分配的
//且沒有遍歷到空閑塊管理鏈表尾部則一直遍歷
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
{
//pxPreviousBlock這里是保存當前空閑塊管理結構體,為了后面找到返回的內(nèi)存地址
pxPreviousBlock = pxBlock;
//指向下一個空閑塊管理結構體
pxBlock = pxBlock->pxNextFreeBlock;
}
/* If we found the end marker then a block of adequate size was not found. */
//pxBlock不等于結尾說明找到符合大小的空閑塊
if( pxBlock != &xEnd )
{
/* Return the memory space - jumping over the BlockLink_t structure
at its start. */
//pvReturn用作返回給用戶,這里要偏移一個空閑塊管理結構體占用內(nèi)存大小
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE );
/* This block is being returned for use so must be taken out of the
list of free blocks. */
//因為pxPreviousBlock->pxNextFreeBlock指向的空閑塊被分配了,
//所以要把pxPreviousBlock->pxNextFreeBlock指向的空閑塊移除出去,
//也就是pxPreviousBlock->pxNextFreeBlock指向pxBlock->pxNextFreeBlock
//也就是跳過分配出去的那個塊
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
/* If the block is larger than required it can be split into two. */
//這里判斷,
//如果將要分配出去的內(nèi)存塊大小xBlockSize比分配出去的還要大heapMINIMUM_BLOCK_SIZE(2倍管理結構體)
//為了節(jié)約就把再分成2塊,一塊返回給用戶,
//一塊構造一個新的空閑管理結構體后插入空閑鏈表
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
{
/* This block is to be split into two. Create a new block
following the number of bytes requested. The void cast is
used to prevent byte alignment warnings from the compiler. */
//注意這里xWantedSize是管理結構體和和真正需要字節(jié)數(shù)之和
//所以是在pxBlock基礎上偏移xWantedSize作為新的管理結構體
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
/* Calculate the sizes of two blocks split from the single
block. */
//pxNewBlockLink新的管理結構體大小
//是待分配pxBlock->xBlockSize-xWantedSize
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
//更新pxBlock->xBlockSize大小為xWantedSize
pxBlock->xBlockSize = xWantedSize;
/* Insert the new block into the list of free blocks. */
//把新構造的空閑管理結構體按xBlockSize大小升序插入到空閑鏈表
prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );
}
//還??臻e字節(jié)數(shù)要減去分配出去的
xFreeBytesRemaining -= pxBlock->xBlockSize;
}
}
traceMALLOC( pvReturn, xWantedSize );
}//解掛調(diào)度器
( void ) xTaskResumeAll();
//如果定義了申請失敗鉤子函數(shù),這里將執(zhí)行
#if( configUSE_MALLOC_FAILED_HOOK == 1 )
{
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
}
#endif
//返回給用戶
return pvReturn;
}其中xFreeBytesRemaining初始化如下
/* Keeps track of the number of free bytes remaining, but says nothing about fragmentation. */ static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE;
初始化內(nèi)存堆
static void prvHeapInit( void )
{
BlockLink_t *pxFirstFreeBlock;
uint8_t *pucAlignedHeap;
/* Ensure the heap starts on a correctly aligned boundary. */
//與heap1操作相同,確保portBYTE_ALIGNMENT字節(jié)對齊,實際使用的首址是pucAlignedHeap
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
/* xStart is used to hold a pointer to the first item in the list of free
blocks. The void cast is used to prevent compiler warnings. */
//空閑鏈表結構體頭部初始化,pxNextFreeBlock指向?qū)嶋H使用的首址pucAlignedHeap
xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
//空閑鏈表結構體頭部沒有可用內(nèi)存,所以xBlockSize是0
xStart.xBlockSize = ( size_t ) 0;
/* xEnd is used to mark the end of the list of free blocks. */
//空閑鏈表結構體尾部初始化,xBlockSize=configADJUSTED_HEAP_SIZE僅僅是為了后面的升序排列,不代表可以空閑字節(jié)數(shù)
xEnd.xBlockSize = configADJUSTED_HEAP_SIZE;
//空閑鏈表結構體尾部初始化,pxNextFreeBlock指向NULL表示結尾
xEnd.pxNextFreeBlock = NULL;
/* To start with there is a single free block that is sized to take up the
entire heap space. */
//第一個空閑塊,pxFirstFreeBlock,即上面xStart指向的pucAlignedHeap
pxFirstFreeBlock = ( void * ) pucAlignedHeap;
//可以空閑內(nèi)存為configADJUSTED_HEAP_SIZE
pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE;
//指向空閑鏈表結構體尾部
pxFirstFreeBlock->pxNextFreeBlock = &xEnd;
}
初始化后的示意圖如下
這里注意xBlockSize是包括管理結構體占用內(nèi)存大小的(出來xStart和xEnd之外,這2個做排序用)

把新構造的結構體插入空閑鏈表
/* STATIC FUNCTIONS ARE DEFINED AS MACROS TO MINIMIZE THE FUNCTION CALL DEPTH. */
/*
* Insert a block into the list of free blocks - which is ordered by size of
* the block. Small blocks at the start of the list and large blocks at the end
* of the list.
*/
#define prvInsertBlockIntoFreeList( pxBlockToInsert ) \
{ \
BlockLink_t *pxIterator; \
size_t xBlockSize; \ \
//這里獲得新構造的空閑結構體成員xBlockSize大小等下用于升序插入
xBlockSize = pxBlockToInsert->xBlockSize; \ \
/* Iterate through the list until a block is found that has a larger size */ \
/* than the block we are inserting. */ \
//從頭開始找到要插入的位置
for( pxIterator = &xStart; pxIterator->pxNextFreeBlock->xBlockSize < xBlockSize; pxIterator = pxIterator->pxNextFreeBlock ) \
{ \
/* There is nothing to do here - just iterate to the correct position. */ \
} \ \
/* Update the list to include the block being inserted in the correct */ \
/* position. */ \
//插入
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; \
pxIterator->pxNextFreeBlock = pxBlockToInsert; \
}
釋放
釋放就很簡單了,就是偏移下地址后直接插入空閑鏈表
void vPortFree( void *pv )
{
uint8_t *puc = ( uint8_t * ) pv;
BlockLink_t *pxLink;
if( pv != NULL )
{
/* The memory being freed will have an BlockLink_t structure immediately
before it. */
//偏移回地址
puc -= heapSTRUCT_SIZE;
/* This unexpected casting is to keep some compilers from issuing
byte alignment warnings. */
pxLink = ( void * ) puc;
//掛起調(diào)度器
vTaskSuspendAll();
{
/* Add this block to the list of free blocks. */
//插入空閑鏈表
prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
//剩余空閑內(nèi)存增加
xFreeBytesRemaining += pxLink->xBlockSize;
traceFREE( pv, pxLink->xBlockSize );
}//解掛調(diào)度器
( void ) xTaskResumeAll();
}
}還剩空閑字節(jié)數(shù)
size_t xPortGetFreeHeapSize( void )
{
return xFreeBytesRemaining;
}
適用范圍、特點
適用于需要釋放的場合,且每次申請釋放的內(nèi)存都是固定大小的,因為釋放時不會合并相鄰空閑內(nèi)存塊,所以如果每次申請釋放都是隨機的,到最后即使剩余內(nèi)存大于要想要分配,由于有很多小的內(nèi)存碎片導致最終分配失敗。
以上就是FreeRTOS動態(tài)內(nèi)存分配管理heap_2示例的詳細內(nèi)容,更多關于FreeRTOS動態(tài)內(nèi)存管理heap_2的資料請關注腳本之家其它相關文章!
相關文章
FreeRTOS實時操作系統(tǒng)在Cortex-M3上的移植過程
這篇文章主要為大家介紹了FreeRTOS實時操作系統(tǒng)在Cortex-M3上的移植過程的示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04
FreeRTOS實時操作系統(tǒng)之可視化追蹤調(diào)試
這篇文章主要為大家介紹了FreeRTOS實時操作系統(tǒng)之可視化追蹤調(diào)試的示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04

