FreeRTOS动态内存分配管理heap_4示例
目录
- heap_4.c 内存堆管理
- 数据结构如下
- 分配
- 内存堆初始化
- 把新构造的结构体插入空闲链表
- 释放
- 还剩空闲字节数
- 历史剩余最小字节数
- 适用范围、特点
heap_4.c 内存堆管理
heap_4也是用链表来管理,但是链表头用的是结构体,链表尾用的是指针,链表尾占用ucHeap
内存
数据结构如下
/* Define the linked list structure. This is used to link free blocks in order of their memory address. */ typedef struct A_BLOCK_LINK { struct A_BLOCK_LINK *pxNextFreeBlock; /*<< The next free block in the list. */ size_t xBlockSize; /*<< The size of the free block. */ } BlockLink_t;
头尾链表如下,注意pxEnd
是指针
/* Create a couple of list links to mark the start and end of the list. */ static BlockLink_t xStart, *pxEnd = NULL;
分配
void *pvPortMalloc( size_t xWantedSize ) { BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink; void *pvReturn = NULL; //挂起调度器,防止函数重入 vTaskSuspendAll(); { /* If this is the first call to malloc then the heap will require initialisation to setup the list of free blocks. */ //pxEnd是NULL则是第一次调用,需要初始化堆 if( pxEnd == NULL ) { prvHeapInit(); } else { mtCOVERAGE_TEST_MARKER(); } /* Check the requested block size is not so large that the top bit is set. The top bit of the block size member of the BlockLink_t structure is used to determine who owns the block - the application or the kernel, so it must be free. */ //xBlockAllocatedBit = 0x8000_0000; //待分配的内存不能大于0x7FFF_FFFF,否则失败 if( ( xWantedSize & xBlockAllocatedBit ) == 0 ) { /* The wanted size is increased so it can contain a BlockLink_t structure in addition to the requested amount of bytes. */ if( xWantedSize > 0 ) { //加上管理结构体占用大小 xWantedSize += xHeapStructSize; /* Ensure that blocks are always aligned to the required number of bytes. */ //xWantedSize大小进行字节对齐调整 if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 ) { /* Byte alignment required. */ xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 ); } else { mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); } //xWantedSize大于0且小于等于此时还剩字节数才能往下申请 if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) ) { /* Traverse the list from the start (lowest address) block until one of adequate size is found. */ //pxPreviousBlock指向头链表 pxPreviousBlock = &xStart; //pxBlock指向头链表的下一个即第一个空闲块 pxBlock = xStart.pxNextFreeBlock; //开始遍历找到第一个比xWantedSize大的空闲块 while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) ) { //pxPreviousBlock保存空闲块的上一个 pxPreviousBlock = pxBlock; pxBlock = pxBlock->pxNextFreeBlock; } /* If the end marker was reached then a block of adequate size was not found. */ //遍历完成pxBlock != pxEnd说明找到符合的空闲块 if( pxBlock != pxEnd ) { /* Return the memory space pointed to - jumping over the BlockLink_t structure at its start. */ //返回给用户的内存地址要跳过管理结构体占用的内存大小 pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize ); /* 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. */ //这里判断, //如果将要分配出去的内存块大小xBlockSize比分配出去的还要大heapMINIMUM_BLOCK_SIZE(2倍管理结构体) //为了节约就把再分成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是管理结构体和和真正需要字节数之和 //所以是在pxBlock基础上偏移xWantedSize作为新的管理结构体 pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 ); /* 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. */ //把新构造的空闲管理结构体按结构体地址升序插入到空闲链表 prvInsertBlockIntoFreeList( pxNewBlockLink ); } else { mtCOVERAGE_TEST_MARKER(); } //还剩空闲字节数要减去分配出去的 xFreeBytesRemaining -= pxBlock->xBlockSize; //更新历史最小剩余字节数 if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining ) { xMinimumEverFreeBytesRemaining = xFreeBytesRemaining; } else { mtCOVERAGE_TEST_MARKER(); } /* The block is being returned - it is allocated and owned by the application and has no "next" block. */ //xBlockSize最高位置1表示被这块内存被分配出去 pxBlock->xBlockSize |= xBlockAllocatedBit; //所以管理结构体的next要指向NULL pxBlock->pxNextFreeBlock = NULL; } else { mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); } traceMALLOC( pvReturn, xWantedSize ); }//解挂调度器 ( void ) xTaskResumeAll(); //如果定义了分配失败钩子函数,分配失败则执行钩子函数 #if( configUSE_MALLOC_FAILED_HOOK == 1 ) { if( pvReturn == NULL ) { extern void vApplicationMallocFailedHook( void ); vApplicationMallocFailedHook(); } else { mtCOVERAGE_TEST_MARKER(); } } #endif //返回给用户 configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 ); return pvReturn; }
内存堆初始化
static void prvHeapInit( void ) { BlockLink_t *pxFirstFreeBlock; uint8_t *pucAlignedHeap; size_t uxAddress; size_t xTotalHeapSize = configTOTAL_HEAP_SIZE; /* Ensure the heap starts on a correctly aligned boundary. */ uxAddress = ( size_t ) ucHeap; //这里进行字节对齐 if( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 ) { uxAddress += ( portBYTE_ALIGNMENT - 1 ); uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK ); //此时xTotalHeapSize表示管理的总内存字节数 xTotalHeapSize -= uxAddress - ( size_t ) ucHeap; } //pucAlignedHeap指向对齐后首址 pucAlignedHeap = ( uint8_t * ) uxAddress; /* 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. */ //初始化头链表 xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap; xStart.xBlockSize = ( size_t ) 0; /* pxEnd is used to mark the end of the list of free blocks and is inserted at the end of the heap space. */ //uxAddress此时指向管理内存最后 uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize; //退回一个BlockLink_t(字节对齐后)大小字节数 uxAddress -= xHeapStructSize; //再次字节对齐 uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK ); //初始化尾链表 pxEnd = ( void * ) uxAddress; pxEnd->xBlockSize = 0; pxEnd->pxNextFreeBlock = NULL; /* To start with there is a single free block that is sized to take up the entire heap space, minus the space taken by pxEnd. */ //初始化第一个空闲块 pxFirstFreeBlock = ( void * ) pucAlignedHeap; //第一个空闲块字节数=uxAddress(此时值=pxEnd) - pxFirstFreeBlock(此时值=pucAlignedHeap) pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock; //第一个空闲块指向尾节点 pxFirstFreeBlock->pxNextFreeBlock = pxEnd; /* Only one block exists - and it covers the entire usable heap space. */ //更新历史还剩最少空闲字节数 xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; //更新实时还剩字节数 xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; /* Work out the position of the top bit in a size_t variable. */ //这里sizeof( size_t ) = 4,heapBITS_PER_BYTE=8,表示1字节有8bit //xBlockAllocatedBit = 1<<(4*8-1) = 0x8000_0000; //FreeRTOS用xBlockSize最高位来标记此内存块是否空闲 //所以heap4最大只能管理0x7FFF_FFFF字节内存 xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 ); }
初始化后的示意图如下
注意xEnd结构体占用的时堆内存
把新构造的结构体插入空闲链表
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert ) { BlockLink_t *pxIterator; uint8_t *puc; /* Iterate through the list until a block is found that has a higher address than the block being inserted. */ //这里是根据内存块的地址大小来迭代寻找和pxBlockToInsert相邻的前一个空闲的内存块 for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock ) { /* Nothing to do here, just iterate to the right position. */ } /* Do the block being inserted, and the block it is being inserted after make a contiguous block of memory? */ //这里判断pxBlockToInsert是否能与pxBlockToInsert相邻的前一个空闲的内存块合并 puc = ( uint8_t * ) pxIterator; if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert ) { //这里做向前合并,xBlockSize相加 pxIterator->xBlockSize += pxBlockToInsert->xBlockSize; //pxBlockToInsert指向pxIterator pxBlockToInsert = pxIterator; } else { mtCOVERAGE_TEST_MARKER(); } /* Do the block being inserted, and the block it is being inserted before make a contiguous block of memory? */ //这里再判断是否能与后一个内存块合并 puc = ( uint8_t * ) pxBlockToInsert; if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock ) { //这里做向后合并,如果要合并的后向不是pxEnd if( pxIterator->pxNextFreeBlock != pxEnd ) { //这里把后项合入到pxBlockToInsert /* Form one big block from the two blocks. */ pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize; //pxBlockToInsert的下一个指向后项指向的空闲块 pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock; } else//如果后项是pxEnd就不能合并,指向pxEnd { pxBlockToInsert->pxNextFreeBlock = pxEnd; } } else//不相邻就只能插入链表 { pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; } /* If the block being inserted plugged a gab, so was merged with the block before and the block after, then it's pxNextFreeBlock pointer will have already been set, and should not be set here as that would make it point to itself. */ //这里如果不等,说明没有做前向合并操作, //需要更新下链表插入 if( pxIterator != pxBlockToInsert ) { pxIterator->pxNextFreeBlock = pxBlockToInsert; } else { mtCOVERAGE_TEST_MARKER(); } }
释放
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 -= xHeapStructSize; /* This casting is to keep the compiler from issuing warnings. */ pxLink = ( void * ) puc; /* Check the block is actually allocated. */ //检查这个内存块是否是heap4之前分配的 configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ); configASSERT( pxLink->pxNextFreeBlock == NULL ); if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ) { if( pxLink->pxNextFreeBlock == NULL ) { /* The block is being returned to the heap - it is no longer allocated. */ //把分配的xBlockSize最高位标记清除 pxLink->xBlockSize &= ~xBlockAllocatedBit; //挂起调度器 vTaskSuspendAll(); { /* Add this block to the list of free blocks. */ //更新剩余内存数 xFreeBytesRemaining += pxLink->xBlockSize; traceFREE( pv, pxLink->xBlockSize ); //插入空闲内存链表 prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); }//解挂调度器 ( void ) xTaskResumeAll(); } else { mtCOVERAGE_TEST_MARKER(); } } else { mtCOVERAGE_TEST_MARKER(); } } }
还剩空闲字节数
size_t xPortGetFreeHeapSize( void ) { return xFreeBytesRemaining; }
历史剩余最小字节数
size_t xPortGetMinimumEverFreeHeapSize( void ) { return xMinimumEverFreeBytesRemaining; }
适用范围、特点
heap4在heap2基础上加入了合并内存碎片算法,把相邻的内存碎片合并成一个更大的块、且xEnd结构体占用的是内存堆空间。
heap2的管理结构体链表是按照xBlockSize大小升序串起来,所以空闲块插入也是按照空闲块大小升序插入,而heap4管理结构体是按照空闲块管理结构体地址大小升序串起来,这样做是为了判断地址是否连续,若连续则能进行碎片合并,且用xBlockSize的最高为标记是否是已经分配的。
以上就是FreeRTOS动态内存分配管理heap_4示例的详细内容,更多关于FreeRTOS动态内存分配的资料请关注我们其它相关文章!
赞 (0)