内存管理
FreeRTOS内核在动态创建任务队列的时候会动态的申请RAM,类似于malloc和free实现动态内存管理,但是它们不适用于小型嵌入式系统,因此FreeRTOS使用自己内核的动态内存管理函数
heap_1内存分配方法
heap_1内存分配方式为移动堆栈空闲起始指针,不允许释放内存。heap_1内存管理办法适合于某些小型的应用,这些应用创建一些任务之后便不会删除。
void *pvPortMalloc( size_t xWantedSize )
{
void *pvReturn = NULL;
static uint8_t *pucAlignedHeap = NULL;
/* portBYTE_ALIGNMENT字节对齐定义,一般为8 */
/* 确字节保对齐;宏定义为8的话,令字节大小向上取8的倍数 */
#if( portBYTE_ALIGNMENT != 1 )
{
if( xWantedSize & portBYTE_ALIGNMENT_MASK )
{
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
}
#endif
vTaskSuspendAll();
{
if( pucAlignedHeap == NULL )
{
/* 确保堆栈开始指针为8的倍数 */
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
}
/* 检查是否有足够的剩余RAM来分配内存 */
if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) &&
( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) )/* Check for overflow. */
{
/* 分配内存,移动堆栈剩余空间的起始指针 */
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 )
{
/* 释放内存并不能回收RAM */
( void ) pv;
configASSERT( pv == NULL );
}
heap_2内存分配方法
定义了一个空闲内存块链表,链表按空闲内存块大小排序;申请内存时遍历此链表,找到合适大小的空闲内存块,将此内存块的地址向后偏移16位,用来存储此内存块的大小信息;在释放内存时,先将指针向前调整,之后根据内存块大小插入到空闲块链表中
heap_2不会把释放的内存合并,随着不断申请和释放内存,可能会造成内存碎片;当每次申请和释放的内存大小一样时,不会造成内存碎片
typedef struct A_BLOCK_LINK //内存块结构体
{
struct A_BLOCK_LINK *pxNextFreeBlock; /* 指向下一个空闲的内存块 */
size_t xBlockSize; /* 当前空闲内存块大小 */
} BlockLink_t;
void *pvPortMalloc( size_t xWantedSize )
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
static BaseType_t xHeapHasBeenInitialised = pdFALSE;
void *pvReturn = NULL;
vTaskSuspendAll();
{
/* 如果时第一次申请内存,初始化空闲内存链表 */
if( xHeapHasBeenInitialised == pdFALSE )
{
prvHeapInit();
xHeapHasBeenInitialised = pdTRUE;
}
/* 判断申请的内存大小 */
if( xWantedSize > 0 )
{
/* 重置申请内存大小,多申请一些内存,在返回的内存起始地址前面存储内存块结构体 */
xWantedSize += heapSTRUCT_SIZE;
/* 保证字节对齐,申请的大小取8的倍数 */
if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0 )
{
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
}
if( ( xWantedSize > 0 ) && ( xWantedSize < configADJUSTED_HEAP_SIZE ) )
{
/* 获取大于申请内存大小空闲内存块,满足条件的内存块指针为pxBlock,链表前一个节点为pxPreviousBlock */
pxPreviousBlock = &xStart;
pxBlock = xStart.pxNextFreeBlock;
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
{
pxPreviousBlock = pxBlock;
pxBlock = pxBlock->pxNextFreeBlock;
}
/* 如果是xEnd,表示没有满足的空闲内存块 */
if( pxBlock != &xEnd )
{
/* 返回申请的内存块指针:满足要求的内存块起始指针偏移16位 */
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE );
/* 清除满足条件的节点,表示被申请了 */
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 ) );
}
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 = ( void * ) puc;
vTaskSuspendAll();
{
/* 将新块加入到空闲块链表中 */
prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
xFreeBytesRemaining += pxLink->xBlockSize;
traceFREE( pv, pxLink->xBlockSize );
}
( void ) xTaskResumeAll();
}
}
heap_3内存分配方法
heap_3内存分配方法就是将malloc和free进行了封装
malloc对比较小的内存申请是直接移动堆栈指针,对大的内存申请使用的是mmap建立文件的内存映射
free函数调用后将内存标记为释放,然后根据情况合并
heap_4内存分配方法
和heap_2分配方式相似,都是定义了一个空闲内存块链表,但是heap_4的是按地址大小来排序的,而且在插入节点时会判断相邻内存块是否可以合并,可以则合并内存块;同时heap_4用位图表示内存块是否被释放,用最高位来表示
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert )
{
BlockLink_t *pxIterator;
uint8_t *puc;
/* 找到要插入的节点位置 */
for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )
{
}
/* 判断内存块是否可以合并,前一个内存块的末地址和要插入的内存块首地址相等则可以插入 */
puc = ( uint8_t * ) pxIterator;
if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
{
/* 合并,直接更改内存块大小即可 */
pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
pxBlockToInsert = pxIterator;
}
else
{
mtCOVERAGE_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
{
mtCOVERAGE_TEST_MARKER();
}
}
heap_5内存分配方法
heap_5和heap_4算法相同,实现也基本相同,只是heap_5允许内存跨越多个不连续的内存段;比如内部RAM和外接RAM一起,但是heap_4只能二选一