FreeRTOS-内存管理源码分析

FreeRTOS 总共提供了5种内存分配方法:heap_1.cheap_2.cheap_3.cheap_4.cheap_5.c这五种分配方式各有各的优势,用户可根据应用情况按需使用,在分析源码之前先了解一下内存管理的一些相关知识,其中内存碎片一直是内存管理致力于解决的一项问题,内存碎片是指频繁地请求和释放不同大小的内存,结果就是当再次要求分配连续的内存时导致申请失败,原因是由于之前...
摘要由CSDN通过智能技术生成

FreeRTOS 总共提供了5种内存分配方法:

heap_1.c
heap_2.c
heap_3.c
heap_4.c
heap_5.c

这五种分配方式各有各的优势,用户可根据应用情况按需使用,在分析源码之前先了解一下内存管理的一些相关知识,其中内存碎片一直是内存管理致力于解决的一项问题,内存碎片是指频繁地请求和释放不同大小的内存,结果就是当再次要求分配连续的内存时导致申请失败,原因是由于之前内存块被释放后,存在空闲内存块大小不一,空闲的小内存块穿插于各个大内存块中间,当需要申请的内存总比这些空闲小内存块大的时候,这些小内存就永远无法得到使用,但它又一直占着空间,这些无法合并的小内存块就叫内存碎片,内存碎片最终会导致内存明明是够的,却无法分配成功的现象。入下图所示:
在这里插入图片描述
FreeRTOS 对内存碎片也有做出相应的处理,后面代码会详细分析源码是如何尽量避免碎片产生的。这里先了解一下5中内存分配的优缺点:

heap_1:
适合不需要动态管理的应用,一旦创建好任务、信号量、队列就不再删除的应用,不会产生内存碎片,对于一般的应用这已经足够使用了

heap_2:
使用动态管理内存,用在可能会重复的删除任务、队列、信号量的应用中,如果每次分配的大小不一 样会导致内存碎片产生,如果针对一项操作每次分配的内存都一样大,那heap2就很合适了

heap_3:
对标准C中的函数 malloc() 和 free() 的简单封装(做线程保护),STM32的话在启动文件中修改Heap_Size来修改内存的大小,这种方式具有不确定性,可能会增加代码量

heap_4:
使用动态管理内存,用在可能会重复的删除任务、队列、信号量的应用中,不会像heap_2那样产生严重的内存碎片,但同样有可能产生内存碎片的问题,只有前后地址连续时才能合并内存块,一定程度上可以降低内存碎片的产生,一般有动态分配需求建议使用这种方式

heap_5:
和heap4的实现方式大致相同,在heap4的基础上允许内存跨越多个不连续的内存段,比如说有外部SRAM或SDRAM的时候,heap4只能选择外部或内部中的一个作为内存管理对象,但heap5则允许两个一起作为内存堆来使用。

在操作内存之前,用户需要自定义内存管理的对象,即内存堆,在 FreeRTOSConfig.c 中可以定义内存堆的大小:

#define configTOTAL_HEAP_SIZE		( ( size_t ) ( 17 * 1024 ) )

系统实际上就是去定义一个大的数组作为内存堆:

static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];

在了解各个分配方式的优缺点后,下面针对每个内存分配方式进行源码分析:


heap_1.c
内存分配源码分析:

void *pvPortMalloc( size_t xWantedSize )
{
void *pvReturn = NULL;
static uint8_t *pucAlignedHeap = NULL;

	#if( portBYTE_ALIGNMENT != 1 )
	{
		/* 字节数8字节对齐 */
		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 ) ) );
		}
		
		/* 检查是否有足够的内存 */
		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 )
{
	/* 使用 heap1 一旦申请内存成功是不允许释放的,所以这里什么也不做 */
	( void ) pv;
	configASSERT( pv == NULL );
}

heap_2.c
内存分配源码分析:

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( ( xWantedSiz
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值