heap_2.c
内存堆管理
heap_2和heap_1一样是开辟一个大数组作为堆空间供用户使用,但是采用单项不循环链表来管理内存的分配释放,主要思想是用链表把内存块串起来,数据结构如下
typedef struct A_BLOCK_LINK
{
//指向下一个空闲内存块管理结构体
struct A_BLOCK_LINK *pxNextFreeBlock;
//记录申请的字节数,包括链表占用所占字节数
size_t xBlockSize;
} BlockLink_t;
与引入链表管理而带来的相关变量如下
//链表结构体对齐后所占字节数
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 ) )
//定义2个局部静态全局结构体变量用于管理
static BlockLink_t xStart, xEnd;
还剩空闲字节数
static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE;
分配
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;
//总的字节数再做此字节对齐
if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0 )
{
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
}
//待分配字节数大于0且小于总共堆字节数
if( ( xWantedSize > 0 ) && ( xWantedSize < configADJUSTED_HEAP_SIZE ) )
{
//pxPreviousBlock指向头链表
pxPreviousBlock = &xStart;
//pxBlock指向第一个开始空闲块
pxBlock = xStart.pxNextFreeBlock;
//当pxBlock所管理的空闲块字节数小于待分配的
//且没有遍历到空闲块管理链表尾部则一直遍历
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
{
//pxPreviousBlock这里是保存当前空闲块管理结构体,为了后面找到返回的内存地址
pxPreviousBlock = pxBlock;
//指向下一个空闲块管理结构体
pxBlock = pxBlock->pxNextFreeBlock;
}
//pxBlock不等于结尾说明找到符合大小的空闲块
if( pxBlock != &xEnd )
{
//pvReturn用作返回给用户,这里要偏移一个空闲块管理结构体占用内存大小
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE );
//因为pxPreviousBlock->pxNextFreeBlock指向的空闲块被分配了,
//所以要把pxPreviousBlock->pxNextFreeBlock指向的空闲块移除出去,
//也就是pxPreviousBlock->pxNextFreeBlock指向pxBlock->pxNextFreeBlock
//也就是跳过分配出去的那个块
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
//这里判断,
//如果将要分配出去的内存块大小xBlockSize比分配出去的还要大heapMINIMUM_BLOCK_SIZE(2倍管理结构体)
//为了节约就把再分成2块,一块返回给用户,
//一块构造一个新的空闲管理结构体后插入空闲链表
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
{
//注意这里xWantedSize是管理结构体和和真正需要字节数之和
//所以是在pxBlock基础上偏移xWantedSize作为新的管理结构体
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
//pxNewBlockLink新的管理结构体大小
//是待分配pxBlock->xBlockSize-xWantedSize
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
//更新pxBlock->xBlockSize大小为xWantedSize
pxBlock->xBlockSize = xWantedSize;
//把新构造的空闲管理结构体按xBlockSize大小升序插入到空闲链表
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;
}
其中xFreeBytesRemaining
初始化如下
static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE;
初始化内存堆
static void prvHeapInit( void )
{
BlockLink_t *pxFirstFreeBlock;
uint8_t *pucAlignedHeap;
//与heap1操作相同,确保portBYTE_ALIGNMENT字节对齐,实际使用的首址是pucAlignedHeap
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
//空闲链表结构体头部初始化,pxNextFreeBlock指向实际使用的首址pucAlignedHeap
xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
//空闲链表结构体头部没有可用内存,所以xBlockSize是0
xStart.xBlockSize = ( size_t ) 0;
//空闲链表结构体尾部初始化,xBlockSize=configADJUSTED_HEAP_SIZE仅仅是为了后面的升序排列,不代表可以空闲字节数
xEnd.xBlockSize = configADJUSTED_HEAP_SIZE;
//空闲链表结构体尾部初始化,pxNextFreeBlock指向NULL表示结尾
xEnd.pxNextFreeBlock = NULL;
//第一个空闲块,pxFirstFreeBlock,即上面xStart指向的pucAlignedHeap
pxFirstFreeBlock = ( void * ) pucAlignedHeap;
//可以空闲内存为configADJUSTED_HEAP_SIZE
pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE;
//指向空闲链表结构体尾部
pxFirstFreeBlock->pxNextFreeBlock = &xEnd;
}
初始化后的示意图如下
这里注意xBlockSize是包括管理结构体占用内存大小的(出来xStart和xEnd之外,这2个做排序用)
把新构造的结构体插入空闲链表
#define prvInsertBlockIntoFreeList( pxBlockToInsert ) \
{ \
BlockLink_t *pxIterator; \
size_t xBlockSize; \ \
//这里获得新构造的空闲结构体成员xBlockSize大小等下用于升序插入
xBlockSize = pxBlockToInsert->xBlockSize; \ \
\
\
//从头开始找到要插入的位置
for( pxIterator = &xStart; pxIterator->pxNextFreeBlock->xBlockSize < xBlockSize; pxIterator = pxIterator->pxNextFreeBlock ) \
{ \
\
} \ \
\
\
//插入
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; \
pxIterator->pxNextFreeBlock = pxBlockToInsert; \
}
释放
释放就很简单了,就是偏移下地址后直接插入空闲链表
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();
}
}
还剩空闲字节数
size_t xPortGetFreeHeapSize( void )
{
return xFreeBytesRemaining;
}
适用范围、特点
适用于需要释放的场合,且每次申请释放的内存都是固定大小的,因为释放时不会合并相邻空闲内存块,所以如果每次申请释放都是随机的,到最后即使剩余内存大于要想要分配,由于有很多小的内存碎片导致最终分配失败。
以上就是FreeRTOS动态内存分配管理heap_2示例的详细内容,更多关于FreeRTOS动态内存管理heap_2的资料请关注编程网其它相关文章!