FreeRTOS中对于内存的管理当前一共有5种实现方式(作者当前的版本是8.2.1),均在【 \Source\portable\MemMang 】下面,这里笔记下。
heap_4.c:
heap_4.c的实现和heap_2.c有一点类似,但是实现了空闲内存合并的功能,减少了内存碎片。
heap_4.c对申请内存的大小有限制,这里作者没有深入探究,后面在看下。
/* |
* A sample implementation of pvPortMalloc() and vPortFree() that combines |
* (coalescences) adjacent memory blocks as they are freed, and in so doing |
* limits memory fragmentation. |
* |
* See heap_1.c, heap_2.c and heap_3.c for alternative implementations, and the |
* memory management pages of http://www.FreeRTOS.org for more information. |
*/ |
#include <stdlib.h> |
/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining |
all the API functions to use the MPU wrappers. That should only be done when |
task.h is included from an application file. */ |
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE |
#include "FreeRTOS.h" |
#include "task.h" |
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE |
/* Block sizes must not get too small. */ |
#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( xHeapStructSize * 2 ) ) |
/* |
1个byte有8个bits |
*/ |
/* Assumes 8bit bytes! */ |
#define heapBITS_PER_BYTE ( ( size_t ) 8 ) |
/* |
内部用来模拟堆的也是一个大号的数组,这里可以通过外部的内存来指定这个大号的数组。 |
*/ |
/* Allocate the memory for the heap. */ |
#if( configAPPLICATION_ALLOCATED_HEAP == 1 ) |
/* The application writer has already defined the array used for the RTOS |
heap - probably so it can be placed in a special segment or address. */ |
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; |
#else |
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; |
#endif /* configAPPLICATION_ALLOCATED_HEAP */ |
/* 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其实也是放在它对应的空余内存的头部的,但是空闲内存的大小并没有考虑放入的BlockLink_t */ |
} BlockLink_t; |
//空闲内存管理结构体,通过它来管理释放回来的内存 |
/*-----------------------------------------------------------*/ |
/* |
* Inserts a block of memory that is being freed into the correct position in |
* the list of free memory blocks. The block being freed will be merged with |
* the block in front it and/or the block behind it if the memory blocks are |
* adjacent to each other. |
*/ |
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert ); |
/* |
* Called automatically to setup the required heap structures the first time |
* pvPortMalloc() is called. |
*/ |
static void prvHeapInit( void ); |
/*-----------------------------------------------------------*/ |
/* 考虑到字节对齐后BlockLink_t的大小 */ |
/* The size of the structure placed at the beginning of each allocated memory |
block must by correctly byte aligned. */ |
static const size_t xHeapStructSize = ( ( sizeof ( BlockLink_t ) + ( ( ( size_t ) portBYTE_ALIGNMENT_MASK ) - ( size_t ) 1 ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK ) ); |
/* 单向链表,注意这里的链表不是以内存大小为依据排序的,而是以内存位置为依据排序的,这样是为了方便空闲内存的合并 */ |
/* Create a couple of list links to mark the start and end of the list. */ |
static BlockLink_t xStart, *pxEnd = NULL; |
/* Keeps track of the number of free bytes remaining, but says nothing about |
fragmentation. */ |
static size_t xFreeBytesRemaining = 0U; |
static size_t xMinimumEverFreeBytesRemaining = 0U; |
/* Gets set to the top bit of an size_t type. When this bit in the xBlockSize |
member of an BlockLink_t structure is set then the block belongs to the |
application. When the bit is free the block is still part of the free heap |
space. */ |
static size_t xBlockAllocatedBit = 0; |
/*-----------------------------------------------------------*/ |
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. */ |
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. */ |
/*这里xWantedSize的大小有要求,需要最高位为0。因为后面BlockLink_t结构体中的xBlockSize的最高位需要使用。 |
*/ |
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; /* 空余内存的头部要放一个BlockLink_t来管理,因此这里需要人为的扩充下申请的内存大小 */ |
/* Ensure that blocks are always aligned to the required number |
of bytes. */ |
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(); |
} |
/* 人为扩充后的大小小于空闲内存 */ |
if ( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) ) |
{ |
/* Traverse the list from the start (lowest address) block until |
one of adequate size is found. */ |
|
//从空余内存链表的头部开始找,如果该空余内存的大小>xWantedSize,就标记该内存为pxBlock |
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. */ |
/* 如果pxBlock不是pxEnd,说明上面的while已经找到了合适的内存 */ |
if ( pxBlock != pxEnd ) |
{ |
/* 找到了,就把该块内存返回给用户,注意内存的头部有BlockLink_t,需要偏移掉 */ |
/* 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 ) > 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 ); |
configASSERT( ( ( ( uint32_t ) pxNewBlockLink ) & portBYTE_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 ) ); |
} |
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,标记了该内存是pvPortMalloc返回的。 |
pxBlock->xBlockSize |= xBlockAllocatedBit; |
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( ( ( ( uint32_t ) pvReturn ) & portBYTE_ALIGNMENT_MASK ) == 0 ); |
return pvReturn; |
} |
/*-----------------------------------------------------------*/ |
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; //这里向前偏移,重新找回BlockLink_t |
/* This casting is to keep the compiler from issuing warnings. */ |
pxLink = ( void * ) puc; |
/* Check the block is actually allocated. */ |
configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ); |
configASSERT( pxLink->pxNextFreeBlock == NULL ); |
|
/* 如果xBlockSize的最高位为1,说明该块内存是pvPortMalloc申请的,那么通过pxBlock->xBlockSize |= xBlockAllocatedBit; |
可以知道该块内存的真实大小为pxLink->xBlockSize &= ~xBlockAllocatedBit,即最高位改回0后的值。 |
*/ |
if ( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ) |
{ |
if ( pxLink->pxNextFreeBlock == NULL ) |
{ |
/* The block is being returned to the heap - it is no longer |
allocated. */ |
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 //如果xBlockSize的最高位不为1,那么说明该块内存不是通过pvPortMalloc申请的,那么直接忽略处理 |
{ |
mtCOVERAGE_TEST_MARKER(); |
} |
} |
} |
/*-----------------------------------------------------------*/ |
size_t xPortGetFreeHeapSize( void ) |
{ |
return xFreeBytesRemaining; |
} |
/*-----------------------------------------------------------*/ |
size_t xPortGetMinimumEverFreeHeapSize( void ) |
{ |
return xMinimumEverFreeBytesRemaining; |
} |
/*-----------------------------------------------------------*/ |
void vPortInitialiseBlocks( void ) |
{ |
/* This just exists to keep the linker quiet. */ |
} |
/*-----------------------------------------------------------*/ |
static void prvHeapInit( void ) |
{ |
BlockLink_t *pxFirstFreeBlock; |
uint8_t *pucAlignedHeap; |
uint32_t ulAddress; |
size_t xTotalHeapSize = configTOTAL_HEAP_SIZE; |
/*根据内存对齐要求获取模拟堆的起始地址(ucHeap可能不符合内存对齐要求,对ucHeap进行内存对齐后赋值给pucAlignedHeap)*/ |
/* Ensure the heap starts on a correctly aligned boundary. */ |
ulAddress = ( uint32_t ) ucHeap; |
if ( ( ulAddress & portBYTE_ALIGNMENT_MASK ) != 0 ) |
{ |
ulAddress += ( portBYTE_ALIGNMENT - 1 ); |
ulAddress &= ~( ( uint32_t ) portBYTE_ALIGNMENT_MASK ); |
xTotalHeapSize -= ulAddress - ( uint32_t ) ucHeap; |
/* xTotalHeapSize表示模拟堆的总内存大小 */ |
} |
pucAlignedHeap = ( uint8_t * ) ulAddress; |
/* 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. */ |
/* |
和heap_2.类似,将当前模拟堆的所有内存构造成一个BlockLink_t,插入空闲单向链表中。 |
不同的是链表的尾部BlockLink_t不是静态的,而是放在了模拟堆空余内存的最后。 |
*/ |
/* 初始化链表头部 */ |
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. */ |
/* |
计算ulAddress,值为模拟堆内存尾部向前偏移一个BlockLink_t的大小,偏移出来的这个BlockLink_t就是pxEnd |
*/ |
ulAddress = ( ( uint32_t ) pucAlignedHeap ) + xTotalHeapSize; |
ulAddress -= xHeapStructSize; |
ulAddress &= ~( ( uint32_t ) portBYTE_ALIGNMENT_MASK ); |
pxEnd = ( void * ) ulAddress; |
pxEnd->xBlockSize = 0; |
pxEnd->pxNextFreeBlock = NULL; |
/* 剩余的所有内存构造成一个BlockLink_t,插入队列中 */ |
/* 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 = ulAddress - ( uint32_t ) pxFirstFreeBlock; |
pxFirstFreeBlock->pxNextFreeBlock = pxEnd; |
/* 更新统计变量 */ |
/* Only one block exists - and it covers the entire usable heap space. */ |
/* |
xMinimumEverFreeBytesRemaining是一个统计值,存储了当前模拟堆运行时的最小剩余内存容量。 |
xFreeBytesRemaining:当前模拟堆的剩余内存容量。 |
*/ |
xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; |
xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; |
/* Work out the position of the top bit in a size_t variable. */ |
/* |
这个xBlockAllocatedBit比较特殊,这里被设置为最高位为1其余为0的一个size_t大小的值, |
这样任意一个size_t大小的值和xBlockAllocatedBit互相&,如果该值最高位为1,那么结果为1,否则结果为0 |
*/ |
xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof ( size_t ) * heapBITS_PER_BYTE ) - 1 ); |
} |
/*-----------------------------------------------------------*/ |
/* |
将一个BlockLink_t的指针插入到单向链表中,注意这里会合并空闲内存 |
*/ |
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert ) |
{ |
BlockLink_t *pxIterator; |
uint8_t *puc; |
/* 首先找到和pxBlockToInsert【前面】相邻的空闲内存 */ |
/* 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; |
/* |
循环结束后,如果找到的内存和pxBlockToInsert相邻,并且在pxBlockToInsert前面,那么就把pxBlockToInsert合并到该内存中 |
*/ |
if ( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert ) //如果一个内存的尾部恰好是pxBlockToInsert头部 |
{ |
pxIterator->xBlockSize += pxBlockToInsert->xBlockSize; //将pxBlockToInsert的大小合入该内存中 |
pxBlockToInsert = pxIterator; |
} |
else |
{ |
//for循环没有找到 |
mtCOVERAGE_TEST_MARKER(); |
} |
/* 判断pxBlockToInsert是否和后面的空闲内存相邻 */ |
/* 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 ) |
{ /* 将后面的内存合入pxBlockToInsert,并用pxBlockToInsert代替该内存在链表中的位置 */ |
/* 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(); |
} |
} |
发表评论