FreeRTOS动态内存分配管理heap_2示例

目录

  • heap_2.c
    • 内存堆管理
  • 分配
    • 初始化内存堆
      • 把新构造的结构体插入空闲链表
        • 释放
          • 还剩空闲字节数
            • 适用范围、特点

              heap_2.c
              内存堆管理
              heap_2和heap_1一样是开辟一个大数组作为堆空间供用户使用,但是采用单项不循环链表来管理内存的分配释放,主要思想是用链表把内存块串起来,数据结构如下
              /* Define the linked list structure.This is used to link free blocks in orderof their size. */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;

              与引入链表管理而带来的相关变量如下
              //链表结构体对齐后所占字节数static const uint16_t heapSTRUCT_SIZE = ( ( sizeof ( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK ); //2倍链表结构体对齐后所占字节数,这作为一个阈值,在分配时起作用#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个局部静态全局结构体变量用于管理static BlockLink_t xStart, xEnd;

              还剩空闲字节数
              /* Keeps track of the number of free bytes remaining, but says nothing aboutfragmentation. */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; //挂起调度器,防止函数重入 vTaskSuspendAll(); {/* If this is the first call to malloc then the heap will requireinitialisation to setup the list of free blocks. *///第一次调用会初始化内存堆if( xHeapHasBeenInitialised == pdFALSE ){prvHeapInit(); xHeapHasBeenInitialised = pdTRUE; }/* The wanted size is increased so it can contain a BlockLink_tstructure in addition to the requested amount of bytes. */if( xWantedSize > 0 ){//用户分配字节数+管理结构体占用字节数xWantedSize += heapSTRUCT_SIZE; /* Ensure that blocks are always aligned to the required number of bytes. *///总的字节数再做此字节对齐if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0 ){/* Byte alignment required. */xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); }}//待分配字节数大于0且小于总共堆字节数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所管理的空闲块字节数小于待分配的//且没有遍历到空闲块管理链表尾部则一直遍历while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) ){//pxPreviousBlock这里是保存当前空闲块管理结构体,为了后面找到返回的内存地址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 structureat its start. *///pvReturn用作返回给用户,这里要偏移一个空闲块管理结构体占用内存大小pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE ); /* This block is being returned for use so must be taken out of thelist 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 blockfollowing the number of bytes requested. The void cast isused to prevent byte alignment warnings from the compiler. *///注意这里xWantedSize是管理结构体和和真正需要字节数之和//所以是在pxBlock基础上偏移xWantedSize作为新的管理结构体pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); /* Calculate the sizes of two blocks split from the singleblock. *///pxNewBlockLink新的管理结构体大小//是待分配pxBlock->xBlockSize-xWantedSizepxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; //更新pxBlock->xBlockSize大小为xWantedSizepxBlock->xBlockSize = xWantedSize; /* Insert the new block into the list of free blocks. *///把新构造的空闲管理结构体按xBlockSize大小升序插入到空闲链表prvInsertBlockIntoFreeList( ( pxNewBlockLink ) ); }//还剩空闲字节数要减去分配出去的xFreeBytesRemaining -= pxBlock->xBlockSize; }}traceMALLOC( pvReturn, xWantedSize ); }//解挂调度器 ( void ) xTaskResumeAll(); //如果定义了申请失败钩子函数,这里将执行 #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 aboutfragmentation. */static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE;


              初始化内存堆
              static void prvHeapInit( void ){BlockLink_t *pxFirstFreeBlock; uint8_t *pucAlignedHeap; /* Ensure the heap starts on a correctly aligned boundary. *///与heap1操作相同,确保portBYTE_ALIGNMENT字节对齐,实际使用的首址是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指向实际使用的首址pucAlignedHeap xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap; //空闲链表结构体头部没有可用内存,所以xBlockSize是0 xStart.xBlockSize = ( size_t ) 0; /* xEnd is used to mark the end of the list of free blocks. */ //空闲链表结构体尾部初始化,xBlockSize=configADJUSTED_HEAP_SIZE仅仅是为了后面的升序排列,不代表可以空闲字节数 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; //可以空闲内存为configADJUSTED_HEAP_SIZE pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE; //指向空闲链表结构体尾部 pxFirstFreeBlock->pxNextFreeBlock = &xEnd; }

              初始化后的示意图如下
              这里注意xBlockSize是包括管理结构体占用内存大小的(出来xStart和xEnd之外,这2个做排序用)
              FreeRTOS动态内存分配管理heap_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 immediatelybefore it. *///偏移回地址puc -= heapSTRUCT_SIZE; /* This unexpected casting is to keep some compilers from issuingbyte alignment warnings. */pxLink = ( void * ) puc; //挂起调度器vTaskSuspendAll(); {/* Add this block to the list of free blocks. *///插入空闲链表prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); //剩余空闲内存增加xFreeBytesRemaining += pxLink->xBlockSize; traceFREE( pv, pxLink->xBlockSize ); }//解挂调度器( void ) xTaskResumeAll(); }}


              还剩空闲字节数
              size_t xPortGetFreeHeapSize( void ){ return xFreeBytesRemaining; }


              适用范围、特点 适用于需要释放的场合,且每次申请释放的内存都是固定大小的,因为释放时不会合并相邻空闲内存块,所以如果每次申请释放都是随机的,到最后即使剩余内存大于要想要分配,由于有很多小的内存碎片导致最终分配失败。
              【FreeRTOS动态内存分配管理heap_2示例】以上就是FreeRTOS动态内存分配管理heap_2示例的详细内容,更多关于FreeRTOS动态内存管理heap_2的资料请关注脚本之家其它相关文章!

                推荐阅读