FreeRTOS代码阅读笔记:heap_4.c

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ZenNaiHeQiao/article/details/82772391

FreeRTOS中对于内存的管理当前一共有5种实现方式(作者当前的版本是10.1.1),均在【 \Source\portable\MemMang 】下面,这里笔记下。

heap_4.c和第二种方式比较相似,只不过增加了一个和并算法,将相邻空闲内存合并为一个大内存,和方法一、二管理策略一样,内存堆仍为一个大数组。

区别在于第四种内存管理策略的空闲块链表不是以内存块大小为存储顺序,而是以内存块起始地址大小为存储顺序,地址小的在前,地址大的在后。这也是为了适应合并算法而作的改变。

使用方法:
头文件:FreeRTOSConfig.h 
配置参数:
configTOTAL_HEAP_SIZE             定义系统所用的堆栈大小。
configUSE_MALLOC_FAILED_HOOK      默认0: 1则开启钩子函数,内存分配失败则调用 
 
函数调用:	
      vPortInitialiseBlocks();//初始化
      ptr=pvPortMalloc(1024);
	  if(ptr !=NULL)
	  {
	  freemem=xPortGetFreeHeapSize(); 
	  printf("剩余内存 %d \r\n",i,freemem); 
	  }
	  else
	  {
	  printf("获取内存失败\r\n");break;

重要参数:

重要的参数备注:
(1)FreeRTOS  内存堆为:ucHeap[] 大小为 configTOTAL_HEAP_SIZE 
(2)pucAlignedHeap 作为堆栈字节对齐后的起始地址(怎么实现的思考一下)
(3)xTotalHeapSize 表示堆栈进行内存大小减去被舍弃字节后的总内存大小
(4)xStart, xEnd; 记录空闲链表的首尾。
(5)pxFirstFreeBlock 空闲链表
(6)xMinimumEverFreeBytesRemaining 是一个统计值,存储了当前模拟堆运行时的最小剩余内存容量
(7)xFreeBytesRemaining:当前模拟堆的剩余内存容量

举个例子:
内存申请:
空闲链是按照地址排序的,从链表中找出内存大小满足的内存块。相对于方式二其查询时间可能变长了。
内存释放:
释放内存后会比较前后的空闲块是否地址连接,是则内存合并。

heap_2.c分析:

将源码解析调整一下顺序:

下面是链表的初始化,heap_2.c中链表的尾部数据并未保存在链表内,是以变量的形式存在的。heap_4.c中的链表尾部数据结构保存在链表空间尾部。

下面是链表初始化代码:

//关于堆栈的初始化
//第一步:起始地址做字节对齐  保存pucAlignedHeap 可用空间大小为xTotalHeapSize 
//第二步:计算首尾 ,这里需要注意的是链表的尾部指针是保存到该地址尾部的
//第三部:完成链表的初始化,记录内存块信息

static void prvHeapInit( void )
{
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链表的头 
	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. */
	//pxEnd链表的尾
	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 );//用来标记内存块是否被使用
}

 空闲块链表的插入:会判断前后的空闲块能否合并,解决内存碎片化的问题。

static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert )
{
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();
	}
}

实现代码

#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. */
}
/*-----------------------------------------------------------*/

猜你喜欢

转载自blog.csdn.net/ZenNaiHeQiao/article/details/82772391