FreeRTOS动态内存分配管理heap_4示例
目录
- heap_4.c 内存堆管理
- 数据结构如下
- 分配
- 内存堆初始化
- 把新构造的结构体插入空闲链表
- 释放
- 还剩空闲字节数
- 历史剩余最小字节数
- 适用范围、特点
heap_4.c 内存堆管理 heap_4也是用链表来管理,但是链表头用的是结构体,链表尾用的是指针,链表尾占用
ucHeap
内存【FreeRTOS动态内存分配管理heap_4示例】
数据结构如下
/* Define the linked list structure.This is used to link free blocks in orderof 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 requireinitialisation 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 isset.The top bit of the block size member of the BlockLink_t structureis used to determine who owns the block - the application or thekernel, 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_tstructure in addition to the requested amount of bytes. */if( xWantedSize > 0 ){//加上管理结构体占用大小xWantedSize += xHeapStructSize; /* Ensure that blocks are always aligned to the required numberof 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 untilone 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 sizewas not found. *///遍历完成pxBlock != pxEnd说明找到符合的空闲块if( pxBlock != pxEnd ){/* Return the memory space pointed to - jumping over theBlockLink_t structure at its start. *///返回给用户的内存地址要跳过管理结构体占用的内存大小pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize ); /* This block is being returned for use so must be taken outof 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 intotwo. *///这里判断,//如果将要分配出去的内存块大小xBlockSize比分配出去的还要大heapMINIMUM_BLOCK_SIZE(2倍管理结构体)//为了节约就把再分成2块,一块返回给用户,//一块构造一个新的空闲管理结构体后插入空闲链表if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ){/* This block is to be split into two.Create a newblock following the number of bytes requested. The voidcast is used to prevent byte alignment warnings from thecompiler. *///注意这里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 thesingle block. *///pxNewBlockLink新的管理结构体大小//是待分配pxBlock->xBlockSize-xWantedSizepxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; //更新pxBlock->xBlockSize大小为xWantedSizepxBlock->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 ownedby the application and has no "next" block. *///xBlockSize最高位置1表示被这块内存被分配出去pxBlock->xBlockSize |= xBlockAllocatedBit; //所以管理结构体的next要指向NULLpxBlock->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指向pxIteratorpxBlockToInsert = 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 ) {//这里做向后合并,如果要合并的后向不是pxEndif( 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 immediatelybefore 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 longerallocated. *///把分配的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动态内存分配的资料请关注脚本之家其它相关文章!
推荐阅读
- FreeRTOS软件定时器apollo中断状态判断
- FreeRTOS动态内存分配管理heap_2示例
- 使用FreeRTOS遇到死等异常的解决
- 实例解析网络编程中的另类内存泄漏
- 操作系统|malloc原理和内存分配
- Linux|malloc 内存分配位置及进程内存布局
- C语言|malloc函数分配内存失败的原因及解决方法
- ARM/linux|malloc内存分配详解
- malloc内存分配原理
- FreeRTOS实时操作系统空闲任务的阻塞延时实现