FreeRTOS--堆内存管理(二)

堆内存管理代码具体实现

  • heap_1
    • 内存申请函数
    • 内存释放函数
  • heap_2
    • 内存块
    • 内存堆初始化函数
    • 内存块插入函数
    • 内存申请函数
      • 判断是不是第一次申请内存
      • 开始分配内存
      • 内存释放函数
  • heap_3
  • heap_4
    • 内存堆初始化函数
    • 内存块插入函数
  • heap_5

上一篇文章说了FreeRTOS实现堆内存的原理,这一篇文章说一下常用函数的代码的具体实现。

heap_1

heap_1的内存堆为ucHeap[],大小为configTOTAL_HEAP_SIZE

内存申请函数

heap_1的内存申请函数pvPortMalloc()源码如下:

/*-----------------------------------------------------------*/void * pvPortMalloc( size_t xWantedSize )
{void * pvReturn = NULL;static uint8_t * pucAlignedHeap = NULL;/* Ensure that blocks are always aligned. */#if ( portBYTE_ALIGNMENT != 1 ){if( xWantedSize & portBYTE_ALIGNMENT_MASK ){/* Byte alignment required. Check for overflow. */if ( (xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) )) > xWantedSize ){xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );}else{xWantedSize = 0;}}}#endifvTaskSuspendAll();{if( pucAlignedHeap == NULL ){/* Ensure the heap starts on a correctly aligned boundary. */pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) & ucHeap[ portBYTE_ALIGNMENT - 1 ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );}/* Check there is enough room left for the allocation and. */if( ( xWantedSize > 0 ) && /* valid size */( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) &&( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) ) /* Check for overflow. */{/* Return the next free byte then increment the index past this* block. */pvReturn = pucAlignedHeap + xNextFreeByte;xNextFreeByte += xWantedSize;}traceMALLOC( pvReturn, xWantedSize );}( void ) xTaskResumeAll();#if ( configUSE_MALLOC_FAILED_HOOK == 1 ){if( pvReturn == NULL ){extern void vApplicationMallocFailedHook( void );vApplicationMallocFailedHook();}}#endifreturn pvReturn;
}
/*-----------------------------------------------------------*/

下面的代码块是判断内存块是否对齐

void * pvPortMalloc( size_t xWantedSize )
{void * pvReturn = NULL;static uint8_t * pucAlignedHeap = NULL;/* Ensure that blocks are always aligned. */#if ( portBYTE_ALIGNMENT != 1 ){if( xWantedSize & portBYTE_ALIGNMENT_MASK ) /***** 令  length = portBYTE_ALIGNMENT_MASK二进制中1的个数*       x = xWantedSize的后length位的值*       y = portBYTE_ALIGNMENT_MASK-x+1*   如果 xWantedSize & portBYTE_ALIGNMENT_MASK = 0,则xWantedSize肯定是(portBYTE_ALIGNMENT_MASK+1)的整数倍*   如果  xWantedSize & portBYTE_ALIGNMENT_MASK不为0,则xWantedSize的后length位肯定有1,xWantedSize不是portBYTE_ALIGNMENT_MASK的整数倍*   如果 xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) )大于xWantedSize,说明后length位肯定有1*   xWantedSize = xWantedSize + y,xWantedSize变成portBYTE_ALIGNMENT整数倍******/{/* Byte alignment required. Check for overflow. */if ( (xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) )) > xWantedSize ){xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );}else{xWantedSize = 0;}}}#endif

#if ( portBYTE_ALIGNMENT != 1 )是判断是否进行字节对齐,portBYTE_ALIGNMENT默认为8:
在这里插入图片描述

#if portBYTE_ALIGNMENT == 32#define portBYTE_ALIGNMENT_MASK    ( 0x001f )
#elif portBYTE_ALIGNMENT == 16#define portBYTE_ALIGNMENT_MASK    ( 0x000f )
#elif portBYTE_ALIGNMENT == 8#define portBYTE_ALIGNMENT_MASK    ( 0x0007 )
#elif portBYTE_ALIGNMENT == 4#define portBYTE_ALIGNMENT_MASK    ( 0x0003 )
#elif portBYTE_ALIGNMENT == 2#define portBYTE_ALIGNMENT_MASK    ( 0x0001 )
#elif portBYTE_ALIGNMENT == 1#define portBYTE_ALIGNMENT_MASK    ( 0x0000 )
#else /* if portBYTE_ALIGNMENT == 32 */#error "Invalid portBYTE_ALIGNMENT definition"

因为portBYTE_ALIGNMENT 定义为8,所以portBYTE_ALIGNMENT_MASK 定义为0x0007,xWantedSize 是无符号整数类型(unsigned int),即32位,xWantedSize 与宏portBYTE_ALIGNMENT_MASK 进行与运算来判断xWantedSize是否为portBYTE_ALIGNMENT 的整数倍,如果等于0就说明xWantedSizr是portBYTE_ALIGNMENT 的整数倍,否则的话将xWantedSize加上一个值,让xWantedSize 变成portBYTE_ALIGNMENT 的整数倍。具体可以看一下代码,我加了注释。
调用vTaskSuspendAll()挂起任务调度器,因为申请内存过程中要做保护,不能被其他任务打断。

pucAlignedHeap是一个静态变量,下面这个代码是初试化pucAlignedHeap,pucAlignedHeap指向ucHeap[ portBYTE_ALIGNMENT - 1 ]

if( pucAlignedHeap == NULL ){/* Ensure the heap starts on a correctly aligned boundary. *//**** 初始化pucAlignedHeap,pucAlignedHeap指向ucHeap[ portBYTE_ALIGNMENT - 1 ]**/pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) & ucHeap[ portBYTE_ALIGNMENT - 1 ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );}

接下来就是分配内存了,最后返回pvReturn,是分配内存的首地址,其中xNextFreeByte是静态变量,表示堆ucHeap已经用了多少内存,configADJUSTED_HEAP_SIZE= configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT,是可以用的内存容量。还有如果剩余内存小于portBYTE_ALIGNMENT,也是不会创建成功的,也就说是,最后的堆可能有内存碎片,下面也一样。

内存释放函数

void vPortInitialiseBlocks( void )
{/* Only required when static memory is not cleared. */xNextFreeByte = ( size_t ) 0;
}

可以看出vPortFree并没有具体释放内存的过程,因此如果使用heap_1,一旦申请内存成功就不允许释放。

heap_2

heap_2的内存堆为ucHeap[],大小为configTOTAL_HEAP_SIZE

内存块

为了实现内存释放,heap_2引入内存块的概念,每分出去的一段内存就是内存块,为了管理内存块,引入了一个链表结构,此链表是来连接空闲块的,链表中的空闲块顺序是根据空闲块内存的大小排列,链表结构如下:

/* Define the linked list structure.  This is used to link free blocks in order* of 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;

为了管理该列表,FreeRTOS定义两个静态变量,xStart、xEnd分别指向链表的头和尾:

/* Create a couple of list links to mark the start and end of the list. */
static BlockLink_t xStart, xEnd;

内存堆初始化函数

内存堆初始化函数为pvHeapInit()

static void prvHeapInit( void )
{BlockLink_t * pxFirstFreeBlock;uint8_t * pucAlignedHeap;/* Ensure the heap starts on a correctly aligned boundary. */pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) & ucHeap[ portBYTE_ALIGNMENT - 1 ] ) & ( ~( ( 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. */xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;xStart.xBlockSize = ( size_t ) 0;/* xEnd is used to mark the end of the list of free blocks. */xEnd.xBlockSize = configADJUSTED_HEAP_SIZE;xEnd.pxNextFreeBlock = NULL;/* To start with there is a single free block that is sized to take up the* entire heap space. */pxFirstFreeBlock = ( void * ) pucAlignedHeap;pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE;pxFirstFreeBlock->pxNextFreeBlock = &xEnd;
}

同heap_1一样,pucAlignedHeap指向ucHeap[ portBYTE_ALIGNMENT - 1 ],xStart.pxNextFreeBlock指向pucAlignedHeap ,内存块大小为0,xEnd指向的内存块大小为configADJUSTED_HEAP_SIZE,pxNextFreeBlock 的值为NULL,第一个内存块pxFirstFreeBlock 指向pucAlignedHeap,大小为configADJUSTED_HEAP_SIZE,pxNextFreeBlock 指向xEnd。

内存块插入函数

#define prvInsertBlockIntoFreeList( pxBlockToInsert )                                                                               \{                                                                                                                               \BlockLink_t * pxIterator;                                                                                                   \size_t 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;                                                                              \}

就是在一个排好序的链表中插入一个数据,xStart最小,for循环一直找到链表中空闲内存块大于pxBlockToInsert内存块大小的内存块,然后插入进去

内存申请函数

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 require* initialisation to setup the list of free blocks. */if( xHeapHasBeenInitialised == pdFALSE ){prvHeapInit();xHeapHasBeenInitialised = pdTRUE;}/* The wanted size must be increased so it can contain a BlockLink_t* structure in addition to the requested amount of bytes. */if( ( xWantedSize > 0 ) &&( ( xWantedSize + heapSTRUCT_SIZE ) >  xWantedSize ) ) /* Overflow check */{xWantedSize += heapSTRUCT_SIZE;/* Byte alignment required. Check for overflow. */if( ( xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ) )> xWantedSize ){xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 );}else{xWantedSize = 0;}}else{xWantedSize = 0;}if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) ){/* Blocks are stored in byte order - traverse the list from the start* (smallest) block until one of adequate size is found. */pxPreviousBlock = &xStart;pxBlock = xStart.pxNextFreeBlock;while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) ){pxPreviousBlock = pxBlock;pxBlock = pxBlock->pxNextFreeBlock;}/* If we found the end marker then a block of adequate size was not found. */if( pxBlock != &xEnd ){/* Return the memory space - jumping over the BlockLink_t structure* at its start. */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 = pxBlock->pxNextFreeBlock;/* If the block is larger than required it can be split into two. */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. */pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );/* Calculate the sizes of two blocks split from the single* block. */pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;pxBlock->xBlockSize = xWantedSize;/* Insert the new block into the list of free blocks. */prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );}xFreeBytesRemaining -= pxBlock->xBlockSize;}}traceMALLOC( pvReturn, xWantedSize );}( void ) xTaskResumeAll();#if ( configUSE_MALLOC_FAILED_HOOK == 1 ){if( pvReturn == NULL ){extern void vApplicationMallocFailedHook( void );vApplicationMallocFailedHook();}}#endifreturn pvReturn;
}

判断是不是第一次申请内存

xHeapHasBeenInitialised是一个静态变量,初始值为pdFALSE,根据xHeapHasBeenInitialised判断是不是第一个调用pvPortMalloc,如果是,即xHeapHasBeenInitialised=pdFALSE,则调用初始化函数prvHeapInit(),然后将xHeapHasBeenInitialised设置为pdTRRE。

开始分配内存

判断xWantedSize是否大于0,不是的话则xWantedSize=0。在大于0 的情况下,xWantedSize要加上heapSTRUCT_SIZE,heapSTRUCT_SIZE是一个static const类型,值为8,是为了来存储一个BlockLink_t结构,同样分配的内存大小也必须是portBYTE_ALIGNMENT的整数倍。

 if( ( xWantedSize > 0 ) &&( ( xWantedSize + heapSTRUCT_SIZE ) >  xWantedSize ) ) /* Overflow check */{xWantedSize += heapSTRUCT_SIZE;/* Byte alignment required. Check for overflow. */if( ( xWantedSize + ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ) )> xWantedSize ){xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 );}else{xWantedSize = 0;}}

如果xWantedSize>0并且xWantedSize要小于xFreeBytesRemaining,xFreeBytesRemaining是一个静态类型变量,表示剩下的可用字节数(所有空闲块大小之和)。
接着按照空闲块大小,从小到大,依次遍历,直到找到一个空闲块,其大小大于等于xWantedSize(此时的xWantedSize=一开始请求的xWantedSize+sizeof(BlockLink_t)),找到满足所需内存块pxBlock,pxPreviousBlock的pxPreviousBlock就指向pxBlock,如果pxBlock不是指向xEnd,就分配内存,pvReturn指向 (pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize的地址,跳过了BlockLink_t,如果pxBlock剩余的内存大于secureheapMINIMUM_BLOCK_SIZE,则将剩余的内存设置成一个新的空闲内存块,新的空闲内存块用 pxNewBlockLink 表示,pxNewBlockLink指向新的内存块的首地址,接着调用prvInsertBlockIntoFreeList,将pxNewBlockLink插入到空闲链表中。

 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. */if( ( xWantedSize & secureportBYTE_ALIGNMENT_MASK ) != 0x00 ){/* Byte alignment required. */xWantedSize += ( secureportBYTE_ALIGNMENT - ( xWantedSize & secureportBYTE_ALIGNMENT_MASK ) );secureportASSERT( ( xWantedSize & secureportBYTE_ALIGNMENT_MASK ) == 0 );}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) ){/* Traverse the list from the start (lowest address) block until* one of adequate size is found. */pxPreviousBlock = &xStart;pxBlock = xStart.pxNextFreeBlock;while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) ){pxPreviousBlock = pxBlock;pxBlock = pxBlock->pxNextFreeBlock;}/* If the end marker was reached then a block of adequate size was* not found. */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 = pxBlock->pxNextFreeBlock;/* If the block is larger than required it can be split into* two. */if( ( pxBlock->xBlockSize - xWantedSize ) > secureheapMINIMUM_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. */pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );secureportASSERT( ( ( ( size_t ) pxNewBlockLink ) & secureportBYTE_ALIGNMENT_MASK ) == 0 );/* Calculate the sizes of two blocks split from the single* block. */pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;pxBlock->xBlockSize = xWantedSize;/* Insert the new block into the list of free blocks. */prvInsertBlockIntoFreeList( pxNewBlockLink );}

接着更新xFreeBytesRemaining,xBlockSize=请求的xWantedSize+sizeof(BlockLink_t)

 xFreeBytesRemaining -= pxBlock->xBlockSize;

最后如果使用hook函数,就调用vApplicationMallocFailedHook()

内存释放函数

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;vTaskSuspendAll();{/* Add this block to the list of free blocks. */prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );xFreeBytesRemaining += pxLink->xBlockSize;traceFREE( pv, pxLink->xBlockSize );}( void ) xTaskResumeAll();}
}

puc为要释放的内存首地址,这就是申请内存返回的pvReturn所指向的地址,所以必须减去heapSTRUCT_SIZE才是要释放的内存段所在内存块的首地址。pxLink 指向真正的释放的内存首地址,将pxLink插入到空闲块链表中,更新xFreeBytesRemaining 。

heap_3

这个分配方法是对标准C库中的mallco和free函数简单封装,FreeRTOS对这两个函数做了线程保护,不分析了。

heap_4

heap_4提供了最优的匹配算法,并且会将碎片合并成一个大的可用内存块,它提供了内存块合并算法

内存堆初始化函数

static void prvHeapInit( void ) /* PRIVILEGED_FUNCTION */
{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 -= uxAddress - ( size_t ) ucHeap;}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 = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize;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;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. */xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
}

首先对申请的内存做字节对齐处理,pucAlignedHeap 为内存堆字节对齐以后的可用起始地址,初始化xStart、pxEnd,同heap_2一样,内存块前面会有一个BlockLink_t类型的变量来描述内存块,这里是完成pxFirstFreeBlock 的初始化,xMinimumEverFreeBytesRemaining 记录最小的空闲内存块大小,xFreeBytesRemaining 表示内存堆剩余大小,初始化静态变量xBlockAllocatedBit

内存块插入函数

static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVILEGED_FUNCTION */
{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. */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? */puc = ( uint8_t * ) pxIterator;if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert ){pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;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 ){if( pxIterator->pxNextFreeBlock != pxEnd ){/* Form one big block from the two blocks. */pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;}else{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();}
}

下面的代码是遍历空闲内存块链表,找出当前内存块插入点,内存块是按照地址从低到高的顺序链接在一起

   for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock ){/* Nothing to do here, just iterate to the right position. */}

找到插入点以后判断是否可以和要插入的内存块合并,如果可以的话就合并在一起,接着检查是否可以和下一个内存块合并,如果可以就再次合并,如果不能就将这两个内存块连接起来,pxIterator不等于pxBlockToInsert就意味着在内存块插入的过程中 没有进行过一次内存合并这样的话就使用最普通的处理方法,pxIterator所指向的内存块在前,pxBlockToinsert所指向的内存块在后,将两个内存块链接起来。

heap_5

heap_5 使用了和heap_4相同的合并算法,内存管理实现起来基本相同,但是heap_5内存堆跨越多个不连续的内存段,在使用heap_5调用API函数之前需要调用vPortDefineHeapRegions来对内存做初始化处理,在vPortDefineHeapRegions未执行之前禁止调用任何可能会调用pvPortMalloc的API函数。vPortDefineHeapRegions的参数是一个HeapRegion_t类型的数组,HeapRegion是一个结构体:

typedef struct HeapRegion
{uint8_t * pucStartAddress;	//内存块的起始地址	size_t xSizeInBytes;	//内存段大小
} HeapRegion_t;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/379703.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在查询的结果中添加自增列 两种方法

解决办法《一》&#xff1a; 在SQL Server数据库中表信息会用到Identity关键字来设置自增列。但是当有数据被删除的话&#xff0c;自增列就不连续了。如果想查询出这个表的信息&#xff0c;并添 加一列连续自增的ID&#xff0c;可用如下查询语句&#xff1a; SELECT Row_Nu…

一个非常简单的C#面试题

怎样实现对所有类可读但是在同一个assembly可写那&#xff1f; 答案&#xff1a; 同一个assembly namespace ClassLibrary1 { public class Class1 { public string Name { get; internal set; } } public class Class2 { public void GS() { Class1 cc new Class1(); cc.Name…

css中的node.js_在Node App中使用基本HTML,CSS和JavaScript

css中的node.jsYou may think this is not important, but it is!. As a beginner in node.js, most coding exercises are always server sided. 您可能认为这并不重要&#xff0c;但确实如此&#xff01; 作为node.js的初学者&#xff0c;大多数编码练习始终都是服务器端的。…

Binary String Matching(C++)

题目描述: Given two strings A and B, whose alphabet consist only ‘0’ and ‘1’. Your task is only to tell how many times does A appear as a substring of B? For example, the text string B is ‘1001110110’ while the pattern string A is ‘11’, you should…

由一次代码优化想到的Js 数据类型

引子&#xff1a; 上周三进行了代码优化&#xff0c;其中有一个很普遍的代码&#xff0c;例如&#xff1a; if(test "") {dothis();}else{dothat()} ----->可以简化为 !test ? dothis():dothat(); if(test "") {dothis()}     ----->可以简化为…

VisualStudio2019配置OpenCV

VisualStudio2019配置OpenCV配置0x01 准备0x02 配置系统环境0x03 复制文件0x04 配置VisualStudio2019测试配置 0x01 准备 下载opencv&#xff0c;官网地址&#xff1a;https://opencv.org/releases/# 下载之后&#xff0c;自行安装 0x02 配置系统环境 找到高级系统设置 …

转载 Javascript的IE和Firefox兼容性汇编

微软关于IE、Firefox、Opera和Safari的JavaScript兼容性研究曾经发表过一份草案,可以点击下载《JScript Deviations from ES3》 以下为网上的一些搜集和整理(FF代表Firefox) 集合类对象问题现有代码中存在许多 document.form.item("itemName") 这样的语句&#xff0c…

存储器间接寻址方式_8086微处理器的程序存储器寻址模式

存储器间接寻址方式The Program Memory Addressing mode is used in branch instructions. These branch instructions are instructions which are responsible for changing the regular flow of the instruction execution and shifting the control to some other location…

Servlet的配置

1&#xff0c;基本配置 <!-- Servlet类的配置 --><servlet><servlet-name>sq</servlet-name><servlet-class>beyond.servlet.QuickStartServlet</servlet-class></servlet><!-- Servlet的虚拟路径的配置 --> <servlet-mapp…

Asp.net页面生存周期

# 事件或方法 功能 描述   1 Init 事件 页面初始化 初始化设置。   2 LoadViewState 方法 加载视图状态 填充ViewState属性。   3 LoadPostData 方法 处理回发数据 处理传入窗体数据。   4 Load 事件 加载页面 页面控件初始化完成并反映了客户端的数据。   5 RaisePo…

你正确关闭WCF链接了吗?

通常情况下我们关闭一个WCF链接都是简单地写把ICommunicationObject.Close()方法&#xff0c;但是这个方法有个问题就是当调用发生异常时&#xff0c;Close()会发生次生的异常&#xff0c;导致链接不能正常关闭。如果当这种异常很多时&#xff0c;必然对系统的稳定性有很大的影…

Visual Studio进行linux远程开发

目录准备工作创建一个项目配置远程项目准备工作 查看linux IP地址 安装了工具 sudo apt-get install openssh-server g gdb make ninja-build rsync zip开启ssh服务&#xff1a; sudo service ssh startVS2019按装了linux功能&#xff0c;如果没有&#xff0c;找到Visual S…

在给定总和K的二叉树中找到级别

Description: 描述&#xff1a; The article describes how to find the level in a binary tree with given sum K? This is an interview coding problem came in Samsung, Microsoft. 本文介绍了如何在给定总和K下在二叉树中找到级别 &#xff1f; 这是一个面试编码问题&a…

PostgreSQL学习手册(数据库维护) 转

原文&#xff1a; PostgreSQL学习手册(数据库维护)一、恢复磁盘空间&#xff1a;在PostgreSQL中&#xff0c;使用delete和update语句删除或更新的数据行并没有被实际删除&#xff0c;而只是在旧版本数据行的物理地址上将该行的状态置为已删除或已过期。因此当数据表中的数据变化…

++i与i++的根本性区别(两个代码对比搞定)

首先来看i 代码如下&#xff1a; #include <stdio.h> #include <stdlib.h> int main() {int i0;int ai;printf("%d\n",a);printf("%d\n\n\n",i);return 0; }输出结果如下&#xff1a; 解释&#xff1a;i其实是两行代码的简写形式&#xff0c…

国企和外企的比较

由于本人在外企&#xff0c;而很多朋友在国企&#xff0c;因此我个人的说法应该还是有一定的权威性。 首先&#xff0c;国企和外企不能一概而论。正如任何事物都有三六九等&#xff0c;这个&#xff0c;只能在同等级别上进行比较。 国企分类&#xff1a; 一等国企&#xff1…

Python | 使用matplotlib.pyplot创建线图

Problem statement: Write a program in python (using matplotlib.pyplot) to create a line plot. 问题陈述&#xff1a;用python编写程序(使用matplotlib.pyplot)以创建线图。 Program: 程序&#xff1a; import matplotlib.pyplot as pltx [1,2,3,4,5,6,7,8,9,10]y [3,…

QI(接口查询)

接触AE一段时间了&#xff0c;总的来说收获不少&#xff0c;今天仔细分析了一下AE开发中经常会用到的QI即接口查询&#xff0c;有了自己的一些理解。 COM类至少有一个接口。事实上一般它们有好几个接口。即一个类经常会实现多个接口&#xff08;一个类无法继承多个类&#xff0…

linux内核设计与实现---从内核出发

获取、编译、安装内核1 获取内核源码安装内核源代码何处安装源码使用补丁2 内核源码树3 编译内核减少编译的垃圾信息衍生多个编译作业安装内核启用指定内核作为引导4 内核开发的特点没有libc库头文件没有内存保护机制容积小而固定的栈1 获取内核源码 在linux内核官方网站http:…

MySQL在DOS下的基本命令操作

启动net start mysql 重置root密码 方法一:在my.ini的[mysqld]字段加入&#xff1a; skip-grant-tables 重启mysql服务&#xff0c;这时的mysql不需要密码即可登录数据库然后进入mysql mysql>use mysql;mysql>更新 user set passwordpassword(新密码) WHERE Userroot; …