当前位置: 首页 > news >正文

FreeRTOS源码学习(一)内存管理heap_1、heap_3

文章目录

  • 0 前言
    • 0.1 前言
    • 0.2 内存分配实现
    • 0.3 参考资料
  • 1 heap_1.c
    • 1.1 应用场景
    • 1.2 源码分析
      • 1.2.1 内存堆
      • 1.2.2 pvPortMalloc
      • 1.2.3 pvPortFree
      • 1.2.4 vPortInitialiseBlocks
      • 1.2.5 xPortGetFreeHeapSize
  • 2 heap_3.c
    • 2.1 应用场景
    • 2.2 源码分析
      • 2.2.1 pvPortMalloc
      • 2.2.2 vPortFree
  • 3 结语

0 前言

0.1 前言

源码分析已经有很多大佬做了,写这篇文章除了巩固自己的学习过程,也想尝试多分析一下FreeRTOS源码的细节之处,比如出于什么考虑使用了这种实现方式

FreeRTOS的内存分配算法有5种,在本文学习heap_1heap_3,是最简单的两个内存分配算法
后面会一起分析heap_2heap_4heap_5

0.2 内存分配实现

FreeRTOS 包含五个内存分配实现示例,分别是 heap_1.cheap_2.cheap_3.cheap_4.cheap_5.c, 位于 Source / Portable / MemMang 目录下

每次一个项目中, 只应包含其中一个源文件

heap_1—— 最简单,不允许释放内存
heap_2—— 允许释放内存,但不会合并相邻的空闲块
heap_3—— 简单包装了标准 malloc() 和 free(),以保证线程安全
heap_4—— 合并相邻的空闲块以避免碎片化。包含绝对地址放置选项
heap_5—— 如同 heap_4,能够跨越多个不相邻内存区域的堆

每个函数的接口函数如下:

void *pvPortMalloc( size_t xWantedSize );
void vPortFree( void *pv );
void vPortInitialiseBlocks( void );
size_t xPortGetFreeHeapSize( void );

0.3 参考资料

FreeRTOS官网 —— 堆内存管理

1 heap_1.c

1.1 应用场景

  • 应用程序从未删除任务、队列、信号量、互斥锁等。
  • 始终具有确定性(总是需要相同的时间来执行), 不会导致内存碎片化。
  • 从静态分配的数组分配内存, 适合用于不允许真实动态内存分配的应用程序 。

1.2 源码分析

必须使能 configSUPPORT_DYNAMIC_ALLOCATION 宏,开启动态内存分配

#if( configSUPPORT_DYNAMIC_ALLOCATION == 0 )#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
#endif

1.2.1 内存堆

内存管理算法管理的内存堆是一个数组,当有应用程序需要申请内存时,内存管理算法提取合适大小的内存供应用程序使用。

#if( configAPPLICATION_ALLOCATED_HEAP == 1 )/* The application writer has already defined the array used for the RTOSheap - probably so it can be placed in a special segment or address. */extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#elsestatic uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif /* configAPPLICATION_ALLOCATED_HEAP */

configAPPLICATION_ALLOCATED_HEAP控制内存堆使用的数组在模块内部,或是在外部定义
ucHeap 为内存堆划分的一段连续的内存空间。前缀 uc代表 unsigned char


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

configTOTAL_HEAP_SIZE FreeRTOSConfig.h文件中定义,是ucHeap 的字节数,用于表示最多可以分配的内存空间

FreeRTOS使用另一个宏定义configADJUSTED_HEAP_SIZE 来表示实际可分配的内存空间大小

/* A few bytes might be lost to byte aligning the heap start address. */
#define configADJUSTED_HEAP_SIZE	( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )

可以看出,实际可分配的内存空间大小就是最多可以分配的内存空间数减去字节对齐数
字节对齐操作可能会导致损失掉一些内存空间,最多损失字节对齐数,这取决于数组的首地址

FreeRTOS这里直接减去字节对齐可能导致的最大损失的字节数,来作为实际可分配的内存空间大小。虽然可能浪费掉一些内存空间,但这样的实现方式可以提高程序整体的可靠性和运行时效率。


static size_t xNextFreeByte = ( size_t ) 0;

xNextFreeByteucHeap 数组的下标,表示下一个空闲元素的位置。前缀x代表 FreeRTOS 自定义的基础数据类型。

1.2.2 pvPortMalloc

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

pvReturn 指向被分配的内存,将作为返回值提供给上层程序

pucAlignedHeap 是用于内存堆管理对齐操作的指针。


	/* Ensure that blocks are always aligned to the required number of bytes. */#if( portBYTE_ALIGNMENT != 1 ){if( xWantedSize & portBYTE_ALIGNMENT_MASK ){/* Byte alignment required. */xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );}}#endif

这里实现了对申请内存空间大小 xWantedSize 的对齐要求,由portBYTE_ALIGNMENT 宏控制
实际上就是向上取portBYTE_ALIGNMENT的倍数

如:xWantedSize 为 101,portBYTE_ALIGNMENT 为 4,则实际申请的内存为 104 字节。

实现内存对齐对于MCU来说有很大的性能提升

  • 提高访问效率:加载一个内存对齐的u32变量仅需一条单周期汇编指令(如LDR)
    如果不是内存对齐,则可能需要调用多个非单周期汇编指令
  • 对齐后可以方便的对指针使用掩码,从而加速一些软件操作(如用掩码实现环形缓冲区)
  • 一些内核(如Cortex-M0)禁止非对齐访问,否则会触发硬件错误
  • 一些外设要求访问地址是对齐的,如有些DMA的传输缓冲区要求是字对齐的

	vTaskSuspendAll();{if( pucAlignedHeap == NULL ){/* Ensure the heap starts on a correctly aligned boundary. */pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );}

这段代码的功能为:在第一次调用pvPortMallo()函数时,初始化 pucAlignedHeap指针,使其指向 ucHeap 数组的某个元素,该地址满足字节对齐要求。

这里使用了掩码的方式实现字节对齐要求

  • 如果一个指针是4字节对齐的,4的二进制为100,那么其指针的低2位一定是全0的。
  • 如果想让任意的一个指针4字节对齐,只要清除其低2位,就可以向下取4字节的倍数,实现4字节对齐
  • 只需要一个简单的&操作,就可以实现字节对齐要求。

在这段代码中:

  • 首先获取了ucHeap[ portBYTE_ALIGNMENT ]的指针,因为在进行字节对齐操作时,不能使对齐后的指针超过ucHeap数组的内存范围,所以不能取ucHeap[0]
  • 然后使用了掩码对其向下向portBYTE_ALIGNMENT 字节取整,获得字节对齐后的指针。
  • 这个指针将作为内存分配的基地址,分配内存空间时从这个地址开始分配。

FreeRTOS在不同场景下实现字节对齐的方式有所差异,届时会特意说明


		/* Check there is enough room left for the allocation. */if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) &&( ( xNextFreeByte + xWantedSize ) > xNextFreeByte )	)/* Check for overflow. */{/* Return the next free byte then increment the index past thisblock. */pvReturn = pucAlignedHeap + xNextFreeByte;xNextFreeByte += xWantedSize;}traceMALLOC( pvReturn, xWantedSize );}( void ) xTaskResumeAll();

这里的if判断有两个条件,第一个条件保证还有剩余内存空间可以分配,第二个条件用于防止输入一个过大的xWantedSize ,计算结果溢出将导致计算结果显著小于实际值,进而可能错误的导致满足了第一个条件。

函数将返回从字节对齐后的内存分配基指针pucAlignedHeap 向后偏移xNextFreeByte个字节的地址作为返回值,代表该部分内存区域被分配给上层程序。


	#if( configUSE_MALLOC_FAILED_HOOK == 1 ){if( pvReturn == NULL ){extern void vApplicationMallocFailedHook( void );vApplicationMallocFailedHook();}}#endifreturn pvReturn;
}

在内存分配失败时,如果使能了回调函数,则会触发内存分配失败回调函数。

1.2.3 pvPortFree

heap_1算法没有内存空间的释放功能,因此该函数没有实际功能。

void vPortFree( void *pv )
{/* Memory cannot be freed using this scheme.  See heap_2.c, heap_3.c andheap_4.c for alternative implementations, and the memory management pages ofhttp://www.FreeRTOS.org for more information. */( void ) pv;/* Force an assert as it is invalid to call this function. */configASSERT( pv == NULL );
}

1.2.4 vPortInitialiseBlocks

初始化内存空间,就是将用于代表已分配字节数的xNextFreeByte变量清零。

void vPortInitialiseBlocks( void )
{/* Only required when static memory is not cleared. */xNextFreeByte = ( size_t ) 0;
}

1.2.5 xPortGetFreeHeapSize

size_t xPortGetFreeHeapSize( void )
{return ( configADJUSTED_HEAP_SIZE - xNextFreeByte );
}

2 heap_3.c

2.1 应用场景

heap_3就是简单包装了C标准库中的 malloc()free()

  • 用于开发环境快速原型验证
  • 与第三方库兼容性要求高的场景
  • 资源受限但需动态内存的短期任务
  • 移植现有非 RTOS 代码到 FreeRTOS

2.2 源码分析

heap_3只有两个接口函数,没有初始化函数和获取剩余可分配内存空间的函数
由于只是封装了C库的内存分配函数,所以没有什么需要特别说明的

2.2.1 pvPortMalloc

void *pvPortMalloc( size_t xWantedSize )
{
void *pvReturn;vTaskSuspendAll();{pvReturn = malloc( xWantedSize );traceMALLOC( pvReturn, xWantedSize );}( void ) xTaskResumeAll();#if( configUSE_MALLOC_FAILED_HOOK == 1 ){if( pvReturn == NULL ){extern void vApplicationMallocFailedHook( void );vApplicationMallocFailedHook();}}#endifreturn pvReturn;
}

2.2.2 vPortFree

void vPortFree( void *pv )
{if( pv ){vTaskSuspendAll();{free( pv );traceFREE( pv, 0 );}( void ) xTaskResumeAll();}
}

3 结语

heap_1heap_3很简单,很适合入门FreeRTOS源码的学习。

下篇文章将分析heap_2heap_4heap_5

http://www.dtcms.com/a/534021.html

相关文章:

  • 成都专业网站建设价格低彩妆网站建设
  • 开发一个电商网站织梦网站logo怎么换
  • FMHY资源导航网页入口 - 全球免费软件与工具合集开源索引
  • 大规模TSP问题的分层聚类预处理中动态确定最优簇数量
  • 哪个网站做动图网站建设发展前景
  • html5制作手机网站制作一个网站
  • 南通模板建站多少钱怎么做网站的浏览栏
  • 宁波网站制作报价扬中吧百度贴吧
  • 用什么软件写网站郑州抖音推广
  • 龙岗区住房和建设局网站打不开只做动漫的网站
  • 做网站输入文本框做下拉idc主机销售系统源码
  • 机器学习19:自监督式学习在语音和影像上的运用
  • 查询网站是否正规vi设计找哪家公司
  • 做网站维护要什么专业大型外包公司有哪些
  • 给网站做公正需要带什么建设政务网站报告
  • 用jsp做的网站代码中国导航电子地图
  • Fedora42 安装Easyconnect
  • ArkTS 中 @State、@Prop 和 @Link 的区别总结
  • 查询网站用什么做的北京厦门网站优化
  • C++ 中 Views 的详细讲解
  • Java基础复习-上
  • 长沙网站改版西安企业建站系统模板
  • 龙岩网站优化个人做网站好吗
  • 无锡网站托管国产一级a做爰片免费网站
  • 巨野住房和城乡建设局网站wordpress多站共享授权码
  • 哪些网站可以做免费广告推广有哪些建设工程类网站
  • 禹城有做网站常见网络营销工具
  • 如何做彩票网站的源码可以做游戏的网站有哪些
  • 龙元建设集团股份有限公司网站地址网站设计素材网站
  • 苏州基础网站建设音乐播放网站开发pc端