一、FreeRTOS的内存管理方式
在FreeRTOS源码中,共有5个关于内存管理的文件,分别对应内存管理的5种方式,它们是heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c。
heap的意思是“堆”,堆就是一块空闲的内存,需要提供管理函数。在C语言中,有malloc与free函数,malloc函数的作用就是从堆里划分出一块空间给程序使用,而free函数的作用是将用完的内存标记为“空闲”,以便下次再用。
与之对应的是“栈”,函数调用时将局部变量保存在栈中,包括当前程序的环境也是保存在栈中,可以从堆中分配一段空间用作栈。
在FreeRTOS中内存管理的接口函数为pvPortMalloc,vPortFree,对应C库的malloc、free。文件在portable目录下,表示你可以提供自己的函数。
二、5种内存管理方式的优缺点
文件 | 优点 | 缺点 |
heap_1.c | 分配简单,时间确定 | 只分配,不回收 |
heap_2.c | 动态分配,最佳匹配 | 碎片,时间不定 |
heap_3.c | 调用标准库函数 | 速度慢,时间不定 |
heap_4.c | 相邻空闲内存可合并 | 解决碎片问题,时间不定 |
heap_5.c | 在heap_4基础上支持分隔的内存块 | 解决碎片问题,时间不定 |
在实际应用中,heap_4是最常用的内存管理方法。
三、5种内存管理方法的学习理解
1、heap_1
heap_1适用于程序不需要删除内核对象的场景,这是因为它只实现了pvPortMalloc,没有实现vPortFree。它的实现最简单,没有碎片问题,在一些要求非常严格的系统里,不允许使用动态内存,就可以使用heap_1
2、heap_2
heap_2还在使用是为了兼容以前的代码,新设计中都是使用heap_4来替代heap_2,更加高效。
heap_2也是在数组上分配内存,和heap_1不一样的是它支持了最佳匹配算法来分配内存,同时它支持了vPortFree。那么什么是最佳匹配算法呢?假设heap有3块空闲内存:5字节、25字节、100字节,vPortMalloc想申请20字节,算法就会负责找出最小同时还能满足申请的字节(这个例子里是25字节),把它划分为20字节和5字节,20字节给申请使用,5字节仍是空闲状态,留给后续使用。
heap_2的缺点是不会合并相邻的空闲内存,所以会导致严重的碎片化问题。同样的,它却适用于频繁的创建、删除任务,前提是任务的栈的大小都是相同的。
3、heap_3
heap_3使用标准C库里的malloc、free函数,所以堆大小由链接器的配置决定,配置项configTOTAL_HEAP_SIZE不再起作用。
C库里的malloc、free函数并非线程安全的,Heap_3中先暂停FreeRTOS的调度器,再去调用这些函数,使用这种方法实现了线程安全。
4、heap_4
heap_4使用首次适应算法来分配内存,还会把相邻的空闲内存合并为一个较大的空闲内存,这有助于较小内存的碎片问题。
举例说明以下首次适应算法:假设堆中有3块空闲内存,分别是5字节、200字节、100字节。pv PortMalloc想申请20字节,找出第一个满足20字节的内存(200字节),把它划分成20字节,180字节,返回这20字节的地址,剩下的180字节仍然是空闲状态,以便后续使用。
heap_4的合并相邻内存的功能使用以下场景:频繁的分配、释放不同大小的内存。
5、heap_5
heap_5分配内存、释放内存的算法和heap_4是一样的,相对于heap_4,heap_5并不局限于管理一个大数组:它可以管理多块、分隔开的内存。当内存的地址并不连续时,这种场景下可以使用heap_5
既然内存是分隔开的,那么就需要进行初始化,确定这些内存块在哪、多大。
- 在使用pvPortMalloc之前,必须先指定内存块的信息
- 使用vPortDefineHeapRegions来指定这些信息