heap2采用链表的方法管理内存堆。链表结构体的next指针指向下一个空闲内存块。并且链表连接的内存块按照从小到大的顺序排列。分配内存的时候,可以遍历查询。
申请内存时,查询到合适大小的内存后,如果内存有多,那么需要割下多余内存,重新插入到内存链表中,按照从小到大的顺序,在合适的位置插入。释放内存的时候,通过释放的地址找到该内存块的链表结构体,给出要释放的内存的大小,并插入到内存堆链表中,同样依照从小到大的顺序。
在heap_2.c 中定义 static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
内存堆为ucHeap[],大小为 configTOTAL_HEAP_SIZE,由宏定义 #define configTOTAL_HEAP_SIZE ((size_t) (40 * 1024)),可以得到内存堆大小为40K字节。
"portmacro.h"
#define portBYTE_ALIGNMENT 8
"portable.h"
#if portBYTE_ALIGNMENT == 8
#define portBYTE_ALIGNMENT_MASK ( 0x0007U )
#endif
void *pvPortMalloc( size_t xWantedSize ){
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
static void prvHeapInit( void ){
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ( portPOINTER_SIZE_TYPE ) ~portBYTE_ALIGNMENT_MASK ) );
}
宏 portBYTE_ALIGNMENT 是需要对齐的字节数,默认为8,需进行8字节对齐也就是说xWantedSize 要为8的倍数,若不是就需要调整为8的倍数。如下:
xWantedSize += ( 8 - (xWantedSize & 0x07) )
eg1. xWantedSize = 30;
xWantedSize = 30 + (8 - (30 & 0x07))= 30 + (8 - 6)= 32
所以需要申请的内存大小应该为32个字节
pucAlignedHeap = ( uint8_t * )( ( uint32_t ) &ucHeap[8] ) & ( ( uint32_t ) ~0x07)
eg2. pucAlignedHeap = 0x200006C4;
pucAlignedHeap = 0x200006CC & 0xFFFFFFFFF8 = 0x200006C8
多以内存块的首地址应该是0x200006C8
typedef struct A_BLOCK_LINK
{
struct A_BLOCK_LINK *pxNextFreeBlock;
size_t xBlockSize;
} BlockLink_t;
为了实现内存释放,heap_2引入了内存块概念,每分出一段内存就是一个内存块,剩下的空闲内存也是一个内存块,内存块大小不定。
每个内存块前面都会有一个BlockLink_t类型的变量来描述此内存块,比如现在申请一个16个字节的内存块。那么就需要申请24个字节的内存块,16字节给申请的内存,另外8个字节来保存BlockLink_t类型结构体变量,xBlockSize记录的是整个内存块的大小。
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 ] ) & ( ( 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;
}
prvHeapInit()
#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; \
}
prvInsertBlockIntoFreeList( 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 is increased so it can contain a BlockLink_t
structure 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 ) );
}
}
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 = &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();
}
}
#endif
return pvReturn;
}
void *pvPortMalloc( size_t xWantedSize )函数
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();
}
}
vPortFree()函数
QP
FreeRTOS
heap2的内存堆大小为configTOTAL_HEAP_SIZE