FreeRTOS源码理解(十一)——内存管理

FreeRTOS源码理解(一)——FreeRTOS思路梳理

FreeRTOS源码理解(二)——中断和列表项

FreeRTOS源码理解(三)——任务

FreeRTOS源码理解(四)——任务调度器

FreeRTOS源码理解(五)——任务切换

FreeRTOS源码理解(六)——时间管理

FreeRTOS源码理解(七)——队列

FreeRTOS源码理解(八)——信号量

FreeRTOS源码理解(九)——事件组和任务通知

FreeRTOS源码理解(十)——软件定时器和空闲任务

FreeRTOS源码理解(十一)——内存管理

FreeRTOS源码理解(十二)——汇总

内存管理

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只能二选一

文档汇总

文档汇总

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值