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,一经查实,立即删除!

相关文章

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…

VisualStudio2019配置OpenCV

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

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…

++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…

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,…

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

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

linux内核设计与实现---进程管理

进程管理1 进程描述符及任务结构分配进程描述符进程描述符的存放进程状态设置当前进程状态进程上下文进程家族树2 进程创建写时拷贝fork()vfork()3 线程在Linux中的实现内核线程4 进程终结删除进程描述符孤儿进程造成的进退微谷5 小结进程的另一个名字叫做任务&#xff08;task…

生日蜡烛(蓝桥杯)

某君从某年开始每年都举办一次生日party&#xff0c;并且每次都要吹熄与年龄相同根数的蜡烛。 现在算起来&#xff0c;他一共吹熄了236根蜡烛。 请问&#xff0c;他从多少岁开始过生日party的&#xff1f; 请填写他开始过生日party的年龄数。 注意&#xff1a;你提交的应该是…

Linux内核设计与实现---进程调度

进程调度1 策略I/O消耗型和处理器消耗型的进程进程优先级时间片进程抢占2 Linux调度算法可执行队列优先级数组重新计算时间片schedule()计算优先级和时间片睡眠和唤醒负载平衡程序3 抢占和上下文切换用户抢占内核抢占4 实时5 与调度相关的系统调用与调度策略和优先级相关的系统…

ServletContext(核心内容)

什么是ServletContext对象 ServletContext代表是一个web应用的环境&#xff08;上下文&#xff09;对象&#xff0c;ServletContext对象 内部封装是该web应用的信息&#xff0c;ServletContext对象一个web应用只有一个 一个web应用有多个servlet对象 ServletContext对象的生…

【转载】[TC]飞船动画例子--《C高级实用程序设计》

【声明和备注】本例子属于转载来源于《C高级实用程序设计》&#xff08;王士元&#xff0c;清华大学出版社&#xff09;第11章&#xff0c;菜单设计与动画技术&#xff0c;第11.5节&#xff0c;一个动画例子。 本例讲解的是在一个繁星背景下&#xff0c;一个由经纬线组成的蓝色…

Linux内核设计与实现---系统调用

系统调用1 API、POSIX和C库2 系统调用系统调用号3 系统调用处理程序指定恰当的系统调用参数传递4 系统调用的实现参数验证5 系统调用上下文绑定一个系统调用的最后步骤从用户空间访问系统调用为什么不通过系统调用的方式实现1 API、POSIX和C库 API&#xff1a;应用编程接口。一…

手动去设置HTTP响应行、响应头、响应体

①手动去设置HTTP响应行中的状态码&#xff0c;这里用到了response的setStatus(int sc);这个方法 package com.itheima.line;import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpSer…

基本的二分查找、寻找第一个和最后一个数的二分查找

二分查找1 二分查找的框架2 寻找一个数&#xff08;基本的二分搜索&#xff09;3 寻找左侧边界的二分搜索4 寻找右侧边界的二分查找5 合并二分查找场景&#xff1a;有序数组寻找一个数、寻找左侧边界&#xff08;有序数组第一个等目标数的下标&#xff09;、寻找右侧边界&#…

Linux内核设计与实现---中断和中断处理程序

中断和中断处理程序1 中断异常2 中断处理程序上半部与下半部的对比3 注册中断处理程序释放中断处理程序4 编写中断处理程序重入和中断处理程序共享的中断处理程序中断处理程序实例5 中断上下文6 中断处理机制的实现7 中断控制禁止和激活中断禁止指定中断线中断系统的状态8 总结…

response细节点

一、 1&#xff09;、response获得的流不需要手动关闭&#xff0c;Tomcat容器会帮你自动关闭 2&#xff09;、getWriter和getOutputStream不能同时调用 //error package com.itheima.content;import java.io.IOException; import javax.servlet.ServletException; import java…

linux内核设计与实现---下半部和推后执行的工作

下半部和推后执行的工作1 下半部为什么要用下半部下半部的环境内核定时器2 软中断软中断的实现软中断处理程序执行软中断使用软中断3 tasklettasklet的实现使用taskletksoftirqd4 工作队列工作队列的实现工作、工作队列和工作者线程之间的关系使用工作队列5 下半部机制的选择6 …

Mac VSCode配置C语言环境(可以调试)

Mac VSCode配置C语言环境c_cpp_properties.jsontasks.jsonlaunch.json新建一个文件夹&#xff0c;用vscode&#xff0c;然后再新建一个test.c文件。 #include <stdio.h>int main(void) {int a1,b1;int cab;printf("%d\n",c);return 0; }这篇文章说怎么配置c_c…