参考:https://blog.csdn.net/zhzht19861011/article/details/51606068
FreeRTOS分为5种内存管理方式,在这里记录对于内存管理方式的理解。
一、heap_1
1、heap_1,只允许管理一个静态的数组ucHeap,内存从静态Ram中由系统分配,不能指定管理外部SRAM,或者管理堆中的内存
2、直接按照申请的内存的大小,从空闲内存中划分内存
3、分配的内存无法回收
这种内存分配方式最简单直接,速度快程序简单。适用于分配完内存后不需要回收的场合。
#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
//heap_1.h的内存管理方式,只允许管理一个静态的数组ucHeap,内存从静态Ram中由系统分配
#if( configSUPPORT_DYNAMIC_ALLOCATION == 0 )
#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
#endif
//首地址按portBYTE_ALIGNMENT对齐后内存容量的大小
#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )
#if( configAPPLICATION_ALLOCATED_HEAP == 1 )
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#else
//heap_1内存分配,直接由Ram中划分一个数组,进行内存管理。
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif
//当前占用内存数
static size_t xNextFreeByte = ( size_t ) 0;
/*-----------------------------------------------------------*/
void *pvPortMalloc( size_t xWantedSize )
{
//此指针指向分配的地址
void *pvReturn = NULL;
//按portBYTE_ALIGNMENT对齐后内存首地址
static uint8_t *pucAlignedHeap = NULL;
//对齐方式不为1的,申请内存数需要按照portBYTE_ALIGNMENT对齐
#if( portBYTE_ALIGNMENT != 1 )
{
//相当于xWantedSize%portBYTE_ALIGNMENT
if( xWantedSize & portBYTE_ALIGNMENT_MASK )
{
//将xWantedSize按照portBYTE_ALIGNMENT强制对齐
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
}
#endif
//挂起任务防止重入
vTaskSuspendAll();
{
//pucAlignedHeap存储按portBYTE_ALIGNMENT对齐后的有效内存首地址
if( pucAlignedHeap == NULL )
{
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
}
//边界检查,检查当前剩余内存容量是否足够分配,且检查xWantedSize是否为正数
if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) &&
( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) )
{
//将剩余内存的首地址返回给用户
pvReturn = pucAlignedHeap + xNextFreeByte;
//当前占用内存数增加
xNextFreeByte += xWantedSize;
}
//用于输出内存分配信息的宏,默认为空不起作用
traceMALLOC( pvReturn, xWantedSize );
}
//恢复任务
( void ) xTaskResumeAll();
#if( configUSE_MALLOC_FAILED_HOOK == 1 )
{
//如果分配内存失败,调用回调函数
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
}
#endif
return pvReturn;
}
/*-----------------------------------------------------------*/
void vPortFree( void *pv )
{
//什么都不做,因为heap_1分配的内存不需要回收
( void ) pv;
configASSERT( pv == NULL );
}
/*-----------------------------------------------------------*/
void vPortInitialiseBlocks( void )
{
//将表示已占用内存数的局部变量清零
//如果清零后再次调用pvPortMalloc();将会出错
//可能导致两个已分配指针使用同一块内存
xNextFreeByte = ( size_t ) 0;
}
/*-----------------------------------------------------------*/
size_t xPortGetFreeHeapSize( void )
{
//用于获取当前剩余可用内存大小
return ( configADJUSTED_HEAP_SIZE - xNextFreeByte );
}
二、heap_2
1、使用链表管理,链表数据域为:有效内存数量 + 结点占用空间
2、链表的头结点、尾结点不存于管理的内存中,其他结点存于管理的内存中。头结点指向内存中首个结点,首结点
指向尾结点。尾结点指针域为Null。首结点数据域为地址对齐后所有有效内存数。
3、内存的申请:
(1) 从链表中找到一个节点,它的数据域大于需要申请的内存数 + 结点占用内存数(不能为尾结点)
(2) 将这个节点从链表中删除,代表它已经分配给用户
(3) 这个节点首地址减去结点本身占用内存之后的地址,将会返回给用户使用
(4) 判断剩余的内存,是否足够再组成一个新的结点
(5) 如果能组成一个结点,则将新结点按照从小到大排列顺序插入到链表,不能则返回
4、内存的释放:
(1) 将用户指针 - 结点占用内存的大小,得到的就是管理此块内存的结点的首地址。
(2) 将此结点插入到链表中
这种分配方式通过链表进行管理,支持内存的释放,支持管理外部SRAM。如果申请跟释放内存的大小是固定的,如32byte。频繁的申请与释放并不会造成碎片,且申请与释放速度快。但如果申请内存不固定,频繁申请与释放将会造成内存碎片。
#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
#if( configSUPPORT_DYNAMIC_ALLOCATION == 0 )
#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
#endif
//首地址按portBYTE_ALIGNMENT对齐后内存容量大小
#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )
static void prvHeapInit( void );
//heap_2允许管理指定地址的内存
#if( configAPPLICATION_ALLOCATED_HEAP == 1 )
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#else
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif
//链表结构体定义
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;
//将链表结构体的大小按照portBYTE_ALIGNMENT进行对齐
static const uint16_t heapSTRUCT_SIZE = ( ( sizeof ( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK );
//一个链表结点,占用内存最小为2倍的结点大小
#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( heapSTRUCT_SIZE * 2 ) )
//创建链表表头跟表尾
static BlockLink_t xStart, xEnd;
//此变量记录空闲内存容量
static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE;
//此宏定义,将一个节点插入链表
//顺序为内存从小到大排列,防止在遍历结点时,用户申请小容量内存却占用大容量内存的结点
#define prvInsertBlockIntoFreeList( pxBlockToInsert ) \
{ \
BlockLink_t *pxIterator; \
size_t xBlockSize; \
\
xBlockSize = pxBlockToInsert->xBlockSize; \
\
\
//遍历链表,找到一个节点pxIterator,它指向的结点的xBlockSize大于或等于需要插入的结点
for( pxIterator = &xStart; pxIterator->pxNextFreeBlock->xBlockSize < xBlockSize; pxIterator = pxIterator->pxNextFreeBlock ) \
{ \
/* There is nothing to do here - just iterate to the correct position. */ \
} \
\
//将需要插入的结点pxBlockToInsert,插入到pxIterator的后面
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; \
pxIterator->pxNextFreeBlock = pxBlockToInsert; \
}
/*-----------------------------------------------------------*/
void *pvPortMalloc( size_t xWantedSize )
{
//定义三个结点,pxBlock为xBlockSize大于等于申请内存数的结点
//pxPreviousBlock为pxBlock上一结点,pxNewBlockLink为从pxBlock分割的新建结点
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
//定义一个静态变量xHeapHasBeenInitialised,初始值为0,用作初始化heap的标志位
static BaseType_t xHeapHasBeenInitialised = pdFALSE;
void *pvReturn = NULL;
vTaskSuspendAll();
{
//判断是否需要初始化heap
if( xHeapHasBeenInitialised == pdFALSE )
{
prvHeapInit();
xHeapHasBeenInitialised = pdTRUE;
}
if( xWantedSize > 0 )
{
//申请的内存,加上结点结构体占用的内存,即为所需的总内存大小(xWantedSize)
xWantedSize += heapSTRUCT_SIZE;
//申请内存数,按照portBYTE_ALIGNMENT_MASK对齐
if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0 )
{
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
}
if( ( xWantedSize > 0 ) && ( xWantedSize < configADJUSTED_HEAP_SIZE ) )
{
//头结点开始遍历链表,找到内存大于或等于xWantedSize的结点pxBlock
//pxPreviousBlock为pxBlock前结点
pxPreviousBlock = &xStart;
pxBlock = xStart.pxNextFreeBlock;
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
{
pxPreviousBlock = pxBlock;
pxBlock = pxBlock->pxNextFreeBlock;
}
//pxBlock不能为尾结点
if( pxBlock != &xEnd )
{
//pxBlock首地址 + 结点结构体大小,即为有效内存首地址
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE );
//pxPreviousBlock的指针域指向pxBlock的下一结点
//相当于将pxBlock结点从链表中删除,因为结点pxBlock已经分配给用户了
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
//判断结点剩余内存是否大于heapMINIMUM_BLOCK_SIZE,大于则新建结点
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
{
//新建结点,首地址为pxBlock剩余内存的首地址
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
//在剩余内存首地址创建一个节点,BlockSize为剩余内存
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
//pxBlock的xBlockSize大小改为xWantedSize
pxBlock->xBlockSize = xWantedSize;
//新的结点插入到链表中
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 vPortFree( void *pv )
{
uint8_t *puc = ( uint8_t * ) pv;
BlockLink_t *pxLink;
if( pv != NULL )
{
//获取此内存块的结点的首地址
puc -= heapSTRUCT_SIZE;
//将结点首地址赋值给pxLink
pxLink = ( void * ) puc;
vTaskSuspendAll();
{
//将此结点插入到空闲链表中
prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
//更新剩余内存数
xFreeBytesRemaining += pxLink->xBlockSize;
traceFREE( pv, pxLink->xBlockSize );
}
( void ) xTaskResumeAll();
}
}
/*-----------------------------------------------------------*/
size_t xPortGetFreeHeapSize( void )
{
return xFreeBytesRemaining;
}
/*-----------------------------------------------------------*/
void vPortInitialiseBlocks( void )
{
/* This just exists to keep the linker quiet. */
}
/*-----------------------------------------------------------*/
//heap初始化函数,初始化头结点与尾结点
//且在有效内存首地址创建一个节点,头结点指向它,它指向尾结点,xBlockSize为对齐后有效内存总数
static void prvHeapInit( void )
{
BlockLink_t *pxFirstFreeBlock;
uint8_t *pucAlignedHeap;
//pucAlignedHeap为内存按portBYTE_ALIGNMENT对齐后的有效内存首地址
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
//链表头结点指向有效内存的首地址
xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
xStart.xBlockSize = ( size_t ) 0;
//链表尾结点指针域为空,数据域为有效内存数
xEnd.xBlockSize = configADJUSTED_HEAP_SIZE;
xEnd.pxNextFreeBlock = NULL;
//在对齐地址后的有效内存首地址,创建一个结点
//结点指针域指向尾结点,数据域为对齐地址后有效内存容量
pxFirstFreeBlock = ( void * ) pucAlignedHeap;
pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE;
pxFirstFreeBlock->pxNextFreeBlock = &xEnd;
}
/*-----------------------------------------------------------*/
三、heap_3
1、此管理方式封装标准库中的内存管理函数,管理的是启动文件中设置的heap。
封装过程中添加的是挂起、恢复调度器的函数,用来保护线程。
此外,它并不能显示我们剩余的空闲内存的大小
四、heap_4
1、跟heap_2一样采用链表进行管理
2、增加了内存块的合并算法,满足条件下先合并前结点再合并后结点,消除内存碎片
3、结点数据域最高位作为标志位,用于检查内存块的状态,为1代表被占用,0则空闲
4、链表中结点按地址从小到大排序,与heap_2的按内存从小到大排列不同
5、因为遍历结点的条件是地址大小。不同于heap_2按照内存大小,所以尾结点放置于内存中,用于结束遍历
#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
#if( configSUPPORT_DYNAMIC_ALLOCATION == 0 )
#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
#endif
#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( xHeapStructSize << 1 ) )
#define heapBITS_PER_BYTE ( ( size_t ) 8 )
#if( configAPPLICATION_ALLOCATED_HEAP == 1 )
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#else
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif
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;
/*-----------------------------------------------------------*/
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert );
static void prvHeapInit( void );
/*-----------------------------------------------------------*/
//结点大小,按portBYTE_ALIGNMENT对齐
static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
//头结点位于Ram中,尾结点位于管理的内存中
static BlockLink_t xStart, *pxEnd = NULL;
//记录可用内存数
static size_t xFreeBytesRemaining = 0U;
//记录剩余内存历史最小值
static size_t xMinimumEverFreeBytesRemaining = 0U;
//用于判断以及改变结点数据域最高位
static size_t xBlockAllocatedBit = 0;
/*-----------------------------------------------------------*/
void *pvPortMalloc( size_t xWantedSize )
{
//pxBlock为分配内存给用户的结点,pxPreviousBlock为pxBlock的前结点,pxNewBlockLink为新建结点
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
//此指针返回分配的内存给用户
void *pvReturn = NULL;
vTaskSuspendAll();
{
//初始化heap
if( pxEnd == NULL )
{
prvHeapInit();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//申请内存数不能影响到标志位
if( ( xWantedSize & xBlockAllocatedBit ) == 0 )
{
if( xWantedSize > 0 )
{
xWantedSize += xHeapStructSize;
//申请内存数按portBYTE_ALIGNMENT对齐
if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
{
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 ) )
{
//找到剩余内存大于申请内存的结点pxBlock
pxPreviousBlock = &xStart;
pxBlock = xStart.pxNextFreeBlock;
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
{
pxPreviousBlock = pxBlock;
pxBlock = pxBlock->pxNextFreeBlock;
}
if( pxBlock != pxEnd )
{
//返回pxBlock中内存的首地址
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );
//pxBlock的前结点改为指向pxBlock的后结点,pxBlock从链表中删除
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
//如果pxBlock剩余内存足够创建一个新的节点
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
{
//新节点首地址为用户申请的内存后的首地址
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
//一个宏定义,如果新建的结点首地址不按照portBYTE_ALIGNMENT对齐,进入死循环
//#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );}
configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 );
//计算新建结点拥有的内存
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
//更新pxBlock的内存块大小
pxBlock->xBlockSize = xWantedSize;
//将新建的结点按照地址从小到大插入到链表
prvInsertBlockIntoFreeList( pxNewBlockLink );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//更新剩余内存大小
xFreeBytesRemaining -= pxBlock->xBlockSize;
//更新最小剩余内存的记录
if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
{
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//pxBlock的数据域最高位置1,代表此内存块被占用
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( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 );
return pvReturn;
}
/*-----------------------------------------------------------*/
void vPortFree( void *pv )
{
uint8_t *puc = ( uint8_t * ) pv;
BlockLink_t *pxLink;
if( pv != NULL )
{
//指针地址减去链表结构体的大小,为指针所在结点的首地址
puc -= xHeapStructSize;
//pxLink为结点首地址
pxLink = ( void * ) puc;
configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );
configASSERT( pxLink->pxNextFreeBlock == NULL );
//如果结点数据域标志位为1,代表此结点内存被占用,进行释放操作
if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )
{
//检查是否为可释放的结点
if( pxLink->pxNextFreeBlock == NULL )
{
//将数据域最高位置0,表示此结点为空闲态
pxLink->xBlockSize &= ~xBlockAllocatedBit;
vTaskSuspendAll();
{
//剩余内存数更新
xFreeBytesRemaining += pxLink->xBlockSize;
traceFREE( pv, pxLink->xBlockSize );
//将此结点插入到链表
prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
}
( void ) xTaskResumeAll();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
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;
size_t uxAddress;
size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;
uxAddress = ( size_t ) ucHeap;
//首地址按照portBYTE_ALIGNMENT对齐
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.pxNextFreeBlock = ( void * ) pucAlignedHeap;
xStart.xBlockSize = ( size_t ) 0;
//内存尾部预留创建一个节点的内存
//因为链表按照地址遍历,所以尾结点放置于内存中
uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize;
uxAddress -= xHeapStructSize;
//地址上移,按照portBYTE_ALIGNMENT对齐
uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
pxEnd = (void *) uxAddress;
pxEnd->xBlockSize = 0;
pxEnd->pxNextFreeBlock = NULL;
//内存首地址创建结点,它的有效内存为内存起始地址到尾结点首地址
pxFirstFreeBlock = ( void * ) pucAlignedHeap;
pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock;
pxFirstFreeBlock->pxNextFreeBlock = pxEnd;
xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
//标志位为size_t数据类型的最高位置1
xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
}
/*-----------------------------------------------------------*/
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert )
{
//定义一个结点指针
BlockLink_t *pxIterator;
uint8_t *puc;
//遍历链表,找到一个结点pxIterator,它指向的结点的地址大于pxBlockToInsert,称为前结点
for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )
{
/* Nothing to do here, just iterate to the right position. */
}
/************************ 结点的合并 **************************/
//如果前结点,跟pxBlockToInsert连续,pxBlockToInsert将被前结点合并
puc = ( uint8_t * ) pxIterator;
if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
{
//被前结点合并,前结点的指针域不需要更新
//前结点数据域增加
pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
//pxBlockToInsert与前结点为同一结点
pxBlockToInsert = pxIterator;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//如果pxBlockToInsert跟找到的结点指向的下一结点(后结点)连续,则pxBlockToInsert合并后结点
puc = ( uint8_t * ) pxBlockToInsert;
if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )
{
if( pxIterator->pxNextFreeBlock != pxEnd )
{
//pxBlockToInsert数据域增加
pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
//pxBlockToInsert合并后结点,被插入结点需要更新指针域
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
}
else
{
//如果跟尾结点连续,则指针域改成指向尾结点
pxBlockToInsert->pxNextFreeBlock = pxEnd;
}
}
/************************ 结点的插入 **************************/
else
{
//如果不连续,pxBlockToInsert指向后结点
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
}
//前结点跟pxBlockToInsert不同,代表前结点跟pxBlockToInsert不连续
if( pxIterator != pxBlockToInsert )
{
//前结点指向pxBlockToInsert
pxIterator->pxNextFreeBlock = pxBlockToInsert;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
五、heap_5
1、内存的申请、释放,结点的插入,与heap_4一致。
2、内存的初始化不同,heap_5可以初始化多个地址不连续的内存块,一同管理。
前一内存块尾结点指向后一内存块头结点,最后一个内存块尾结点为Null
3、初始化之前,需要用户自定义内存块地址以及大小
4、初始化函数不能重复运行,以及未初始化之前不能申请内存,否则都会进入死循环
#include <stdlib.h>
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE
#include "FreeRTOS.h"
#include "task.h"
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
#if( configSUPPORT_DYNAMIC_ALLOCATION == 0 )
#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
#endif
#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( xHeapStructSize << 1 ) )
#define heapBITS_PER_BYTE ( ( size_t ) 8 )
///* Used by heap_5.c. */
//typedef struct HeapRegion
//{
// uint8_t *pucStartAddress; //指向一个地址
// size_t xSizeInBytes; //此内存块的大小
//} HeapRegion_t;
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;
/*-----------------------------------------------------------*/
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert );
/*-----------------------------------------------------------*/
//链表结构体大小按照portBYTE_ALIGNMENT对齐
static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
//定义头结点、尾结点指针,尾结点位于内存中
static BlockLink_t xStart, *pxEnd = NULL;
//记录剩余内存
static size_t xFreeBytesRemaining = 0U;
//记录历史最小剩余内存
static size_t xMinimumEverFreeBytesRemaining = 0U;
//结点内存状态位标志
static size_t xBlockAllocatedBit = 0;
/*-----------------------------------------------------------*/
void *pvPortMalloc( size_t xWantedSize )
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
void *pvReturn = NULL;
//pxEnd为空证明未执行初始化,进入死循环
configASSERT( pxEnd );
vTaskSuspendAll();
{
//申请内存数不能大于2GB,影响内存状态标志位
if( ( xWantedSize & xBlockAllocatedBit ) == 0 )
{
//申请内存数加上结点占用内存后,按照portBYTE_ALIGNMENT对齐
if( xWantedSize > 0 )
{
xWantedSize += xHeapStructSize;
if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
{
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
{
//遍历结点,找到内存数大于等于申请内存数的结点pxBlock,将其分配给用户
pxPreviousBlock = &xStart;
pxBlock = xStart.pxNextFreeBlock;
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
{
pxPreviousBlock = pxBlock;
pxBlock = pxBlock->pxNextFreeBlock;
}
if( pxBlock != pxEnd )
{
//pxBlock中的内存首地址返回给用户
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );
//pxBlock的前结点改为指向pxBlock的后结点,pxBlock从链表中删除
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
//如果pxBlock中剩余内存大于新建一个节点所需内存
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
{
//新建一个节点,位于pxBlock剩余内存的首地址
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
//计算新建结点的内存数
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
//更新pxBlock的内存数
pxBlock->xBlockSize = xWantedSize;
//将新建的结点插入到链表,顺序为按照地址从小到大
prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//更新剩余内存数
xFreeBytesRemaining -= pxBlock->xBlockSize;
//更新最小剩余内存历史
if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
{
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//pxBlock数据域最高位置1,标志此结点内存被占用
pxBlock->xBlockSize |= xBlockAllocatedBit;
//被分配的结点,指针域设为Null
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
return pvReturn;
}
/*-----------------------------------------------------------*/
void vPortFree( void *pv )
{
uint8_t *puc = ( uint8_t * ) pv;
BlockLink_t *pxLink;
if( pv != NULL )
{
//计算指针所处结点的首地址
puc -= xHeapStructSize;
//pxLink存放指针所处结点首地址
pxLink = ( void * ) puc;
//抛出异常
configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );
configASSERT( pxLink->pxNextFreeBlock == NULL );
//如果结点的数据域标志位为1,进行释放操作
if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )
{
//如果结点的指针域为空
if( pxLink->pxNextFreeBlock == NULL )
{
//数据域标志位更新为0
pxLink->xBlockSize &= ~xBlockAllocatedBit;
vTaskSuspendAll();
{
//更新空闲内存数
xFreeBytesRemaining += pxLink->xBlockSize;
traceFREE( pv, pxLink->xBlockSize );
//将结点插入到链表,顺序为按地址从小到大
prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
}
( void ) xTaskResumeAll();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
/*-----------------------------------------------------------*/
size_t xPortGetFreeHeapSize( void )
{
return xFreeBytesRemaining;
}
/*-----------------------------------------------------------*/
size_t xPortGetMinimumEverFreeHeapSize( void )
{
return xMinimumEverFreeBytesRemaining;
}
/*-----------------------------------------------------------*/
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert )
{
BlockLink_t *pxIterator;
uint8_t *puc;
//遍历链表,找到一个节点pxIterator,它指向的结点的地址大于pxBlockToInsert,称为前结点
for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )
{
/* Nothing to do here, just iterate to the right position. */
}
//如果pxBlockToInsert,与前结点连续,则合并两个结点,合并后结点首地址为前结点首地址
puc = ( uint8_t * ) pxIterator;
if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
{
//更新合并后的结点的数据域
pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
//pxBlockToInsert的指针域,指向合并后结点的首地址
pxBlockToInsert = pxIterator;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//如果pxBlockToInsert,与前结点指向的结点(后结点)连续,则合并两个结点,合并后结点首地址为pxBlockToInsert的首地址
puc = ( uint8_t * ) pxBlockToInsert;
if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )
{
if( pxIterator->pxNextFreeBlock != pxEnd )
{
//更新pxBlockToInsert的数据域
pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
//更新pxBlockToInsert的指针域
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
}
else
{
//后结点为尾结点,pxBlockToInsert指向尾结点
pxBlockToInsert->pxNextFreeBlock = pxEnd;
}
}
else
{
//如果pxBlockToInsert与后结点不连续,pxBlockToInsert指向后结点
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
}
//如果前结点与pxBlockToInsert没有合并
if( pxIterator != pxBlockToInsert )
{
//前结点指向pxBlockToInsert,pxBlockToInsert成功插入到链表中
pxIterator->pxNextFreeBlock = pxBlockToInsert;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/*-----------------------------------------------------------*/
//传递的结构体指针不可修改,且指针指向的内存也不可修改
void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions )
{
//pxFirstFreeBlockInRegion缓存当前内存块的头结点地址,pxPreviousFreeBlock缓存上一个内存块尾结点地址
BlockLink_t *pxFirstFreeBlockInRegion = NULL, *pxPreviousFreeBlock;
//记录对齐地址后内存块首地址
size_t xAlignedHeap;
//xTotalRegionSize代表的是本内存块总的内存数,xTotalHeapSize代表所有内存块内存数
size_t xTotalRegionSize, xTotalHeapSize = 0;
//记录结构体数组中的元素,从元素0到元素n,逐个初始化
BaseType_t xDefinedRegions = 0;
size_t xAddress;
//记录结构体数组每个元素的首地址
const HeapRegion_t *pxHeapRegion;
//尾结点不为Null进入死循环
configASSERT( pxEnd == NULL );
//取结构体数组第一个元素首地址
pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );
//初始化结构体数组,当数组最后一个元素的xSizeInBytes为0,代表所有内存块初始化完毕
//xSizeInBytes不为0则持续初始化内存块
while( pxHeapRegion->xSizeInBytes > 0 )
{
//记录单个内存块的内存大小
xTotalRegionSize = pxHeapRegion->xSizeInBytes;
//记录当前内存块的首地址
xAddress = ( size_t ) pxHeapRegion->pucStartAddress;
//地址按照portBYTE_ALIGNMENT对齐
if( ( xAddress & portBYTE_ALIGNMENT_MASK ) != 0 )
{
xAddress += ( portBYTE_ALIGNMENT - 1 );
xAddress &= ~portBYTE_ALIGNMENT_MASK;
//更新内存大小
xTotalRegionSize -= xAddress - ( size_t ) pxHeapRegion->pucStartAddress;
}
//记录经过对齐后当前内存块的首地址
xAlignedHeap = xAddress;
//如果当前为结构体数组第0元素
if( xDefinedRegions == 0 )
{
//头结点初始化,指向此内存块首地址
xStart.pxNextFreeBlock = ( BlockLink_t * ) xAlignedHeap;
xStart.xBlockSize = ( size_t ) 0;
}
else //否则检查尾结点与结构体数组元素的地址是否异常
{
configASSERT( pxEnd != NULL );
configASSERT( xAddress > ( size_t ) pxEnd );
}
//指向尾结点,当初始化第二块内存块
//此指针指向的是上一内存块尾结点
pxPreviousFreeBlock = pxEnd;
//在此内存块尾部创建一个结点
xAddress = xAlignedHeap + xTotalRegionSize;
xAddress -= xHeapStructSize;
xAddress &= ~portBYTE_ALIGNMENT_MASK;
//尾结点指针指向当前内存块尾部
pxEnd = ( BlockLink_t * ) xAddress;
pxEnd->xBlockSize = 0;
pxEnd->pxNextFreeBlock = NULL;
//指向当前内存块首地址创建一个结点,此结点称为当前内存块首结点
pxFirstFreeBlockInRegion = ( BlockLink_t * ) xAlignedHeap;
//将当前内存块的内存数装载进首结点数据域
pxFirstFreeBlockInRegion->xBlockSize = xAddress - ( size_t ) pxFirstFreeBlockInRegion;
//首结点指向当前内存块尾结点
pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd;
//当初始化第一个内存块,下面语句不被执行
if( pxPreviousFreeBlock != NULL )
{
//让前一内存块的尾结点指向后一内存块的首结点
pxPreviousFreeBlock->pxNextFreeBlock = pxFirstFreeBlockInRegion;
}
//初始化完毕一个内存块,更新总内存块的内存大小
xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize;
xDefinedRegions++;
//指向结构体数组中下一个元素,对下一内存块进行初始化
pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );
}
//更新剩余内存数,以及历史最小剩余内存数
xMinimumEverFreeBytesRemaining = xTotalHeapSize;
xFreeBytesRemaining = xTotalHeapSize;
configASSERT( xTotalHeapSize );
//根据size_t数据类型占用字节数更新结点内存状态位标志
xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
}
六、myMalloc
将heap_5修改了一下,可脱离FreeRTOS使用。方便进行内存管理
头文件
#ifndef __bsp_Malloc_H
#define __bsp_Malloc_H
#include <stdlib.h>
#include "stm32f1xx_hal.h"
//定义管理的内存块的起始地址与大小
#define Ram1Addr (0x68000000UL)
#define Ram1Size (960*1024)
//定义中断屏蔽优先级
#define configMAX_INTERRUPT_PRIORITY 2
#ifndef TEST_MARKER
#define TEST_MARKER()
#endif
#ifndef myFORCE_INLINE
#define myFORCE_INLINE __forceinline
#endif
typedef struct myHeapRegion
{
uint8_t *pucStartAddress; //指向一个地址
size_t xSizeInBytes; //此内存块的大小
} myHeapRegion_t;
extern myHeapRegion_t xHeapRegions[];
void *myMalloc( size_t xWantedSize );
void myFree( void *pv );
size_t myGetFreeHeapSize( void );
size_t myGetMinimumEverFreeHeapSize( void );
void myDefineHeapRegions( const myHeapRegion_t * const pxHeapRegions );
#endif
源文件
#include "bsp_Malloc.h"
typedef long BaseType_t;
#define configUSE_MALLOC_FAILED_HOOK 0
#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( xHeapStructSize << 1 ) )
#define heapBITS_PER_BYTE ( ( size_t ) 8 )
#define portBYTE_ALIGNMENT 8
#define portBYTE_ALIGNMENT_MASK (portBYTE_ALIGNMENT - 1)
#define MallocInterruptMask( ) vPortSetBASEPRI( )
#define MallocInterruptRelease( ) vPortRaiseBASEPRI( )
#define mallocASSERT( x ) if ((x) == 0) { for( ;; );}
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;
myHeapRegion_t xHeapRegions[] =
{
{ ( uint8_t * ) Ram1Addr, Ram1Size },
{ NULL, 0 }
};
/*-----------------------------------------------------------*/
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert );
/*-----------------------------------------------------------*/
static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
static BlockLink_t xStart, *pxEnd = NULL;
static size_t xFreeBytesRemaining = 0U;
static size_t xMinimumEverFreeBytesRemaining = 0U;
static size_t xBlockAllocatedBit = 0;
/*-----------------------------------------------------------*/
// 开中断
// 向basepri中写入0就表示开中断
static myFORCE_INLINE void vPortSetBASEPRI( void )
{
__asm
{
msr basepri, 0
}
}
// 关中断
// 向basepri中写入configMAX_INTERRUPT_PRIORITY,
// 表明优先级低于configMAX_INTERRUPT_PRIORITY的中断都会被屏蔽
static myFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_INTERRUPT_PRIORITY;
__asm
{
msr basepri, ulNewBASEPRI
dsb
isb
}
}
void *myMalloc( size_t xWantedSize )
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
void *pvReturn = NULL;
mallocASSERT( pxEnd );
MallocInterruptMask();
{
if( ( xWantedSize & xBlockAllocatedBit ) == 0 )
{
if( xWantedSize > 0 )
{
xWantedSize += xHeapStructSize;
if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
{
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
else
{
TEST_MARKER();
}
}
else
{
TEST_MARKER();
}
if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
{
pxPreviousBlock = &xStart;
pxBlock = xStart.pxNextFreeBlock;
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
{
pxPreviousBlock = pxBlock;
pxBlock = pxBlock->pxNextFreeBlock;
}
if( pxBlock != pxEnd )
{
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
{
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
pxBlock->xBlockSize = xWantedSize;
prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );
}
else
{
TEST_MARKER();
}
xFreeBytesRemaining -= pxBlock->xBlockSize;
if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
{
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
}
else
{
TEST_MARKER();
}
pxBlock->xBlockSize |= xBlockAllocatedBit;
pxBlock->pxNextFreeBlock = NULL;
}
else
{
TEST_MARKER();
}
}
else
{
TEST_MARKER();
}
}
else
{
TEST_MARKER();
}
}
MallocInterruptRelease();
#if( configUSE_MALLOC_FAILED_HOOK == 1 )
{
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
else
{
TEST_MARKER();
}
}
#endif
return pvReturn;
}
/*-----------------------------------------------------------*/
void myFree( void *pv )
{
uint8_t *puc = ( uint8_t * ) pv;
BlockLink_t *pxLink;
if( pv != NULL )
{
puc -= xHeapStructSize;
pxLink = ( void * ) puc;
mallocASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );
mallocASSERT( pxLink->pxNextFreeBlock == NULL );
if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )
{
if( pxLink->pxNextFreeBlock == NULL )
{
pxLink->xBlockSize &= ~xBlockAllocatedBit;
MallocInterruptMask();
{
xFreeBytesRemaining += pxLink->xBlockSize;
prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
}
MallocInterruptRelease();
}
else
{
TEST_MARKER();
}
}
else
{
TEST_MARKER();
}
}
}
/*-----------------------------------------------------------*/
size_t myGetFreeHeapSize( void )
{
return xFreeBytesRemaining;
}
/*-----------------------------------------------------------*/
size_t myGetMinimumEverFreeHeapSize( void )
{
return xMinimumEverFreeBytesRemaining;
}
/*-----------------------------------------------------------*/
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert )
{
BlockLink_t *pxIterator;
uint8_t *puc;
for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )
{
/* Nothing to do here, just iterate to the right position. */
}
puc = ( uint8_t * ) pxIterator;
if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
{
pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
pxBlockToInsert = pxIterator;
}
else
{
TEST_MARKER();
}
puc = ( uint8_t * ) pxBlockToInsert;
if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )
{
if( pxIterator->pxNextFreeBlock != pxEnd )
{
pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
}
else
{
pxBlockToInsert->pxNextFreeBlock = pxEnd;
}
}
else
{
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
}
if( pxIterator != pxBlockToInsert )
{
pxIterator->pxNextFreeBlock = pxBlockToInsert;
}
else
{
TEST_MARKER();
}
}
/*-----------------------------------------------------------*/
void myDefineHeapRegions( const myHeapRegion_t * const pxHeapRegions )
{
BlockLink_t *pxFirstFreeBlockInRegion = NULL, *pxPreviousFreeBlock;
size_t xAlignedHeap;
size_t xTotalRegionSize, xTotalHeapSize = 0;
BaseType_t xDefinedRegions = 0;
size_t xAddress;
const myHeapRegion_t *pxHeapRegion;
mallocASSERT( pxEnd == NULL );
pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );
while( pxHeapRegion->xSizeInBytes > 0 )
{
xTotalRegionSize = pxHeapRegion->xSizeInBytes;
xAddress = ( size_t ) pxHeapRegion->pucStartAddress;
if( ( xAddress & portBYTE_ALIGNMENT_MASK ) != 0 )
{
xAddress += ( portBYTE_ALIGNMENT - 1 );
xAddress &= ~portBYTE_ALIGNMENT_MASK;
xTotalRegionSize -= xAddress - ( size_t ) pxHeapRegion->pucStartAddress;
}
xAlignedHeap = xAddress;
if( xDefinedRegions == 0 )
{
xStart.pxNextFreeBlock = ( BlockLink_t * ) xAlignedHeap;
xStart.xBlockSize = ( size_t ) 0;
}
else
{
mallocASSERT( pxEnd != NULL );
mallocASSERT( xAddress > ( size_t ) pxEnd );
}
pxPreviousFreeBlock = pxEnd;
xAddress = xAlignedHeap + xTotalRegionSize;
xAddress -= xHeapStructSize;
xAddress &= ~portBYTE_ALIGNMENT_MASK;
pxEnd = ( BlockLink_t * ) xAddress;
pxEnd->xBlockSize = 0;
pxEnd->pxNextFreeBlock = NULL;
pxFirstFreeBlockInRegion = ( BlockLink_t * ) xAlignedHeap;
pxFirstFreeBlockInRegion->xBlockSize = xAddress - ( size_t ) pxFirstFreeBlockInRegion;
pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd;
if( pxPreviousFreeBlock != NULL )
{
pxPreviousFreeBlock->pxNextFreeBlock = pxFirstFreeBlockInRegion;
}
xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize;
xDefinedRegions++;
pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );
}
xMinimumEverFreeBytesRemaining = xTotalHeapSize;
xFreeBytesRemaining = xTotalHeapSize;
mallocASSERT( xTotalHeapSize );
xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
}
程序在原子战舰V3通过测试:
观察断点,可知 j 比 k 大0x38,申请内存数为4、11、4,进行对齐后应该是8、16、8,加上3个结点占用的空间:24
共占用空间:56 = 0x38。 全部内存释放后,容量为0xEFFF8,刚好为:0xF0000 - 0x08。丢失的8字节被尾结点占用。