前面介绍了FreeRTOS堆内存管理实现,总共有5种实现分别在heap_1.c,heap_2.c,heap_3.c,heap_4.c和heap_5.c文件中,都存在于文件夹FreeRTOS/Source/portable/MemMang下。上一讲讲了第一个实现Heap_1。
我们再看一下第二个实现Heap_2:
为了向后兼容性,Heap_2保留在FreeRTOS发行版中,但是不推荐在新设计使用它。可以考虑使用heap_4,heap_4是heap_2增强版。
Heap_2.c还是通过configTOTAL_HEAP_SIZE来定义堆的大小,它使用最佳匹配算法来分配内存,并且与heap_1不同,它允许释放内存。同样,数组是静态声明的,因此会使应用程序看起来消耗大量RAM,甚至在分配数组中的任何内存之前也是如此。
最佳匹配算法确保pvPortMalloc()使用大小与请求字节数最接近的空闲内存块。例如,以下场景:
1. 堆分别包含3块空闲内存,分别是5个字节、25个字节和100个字节;
2. 调用pvPortMalloc()请求20个字节的RAM。
所请求的字节数能够容纳的最小空闲RAM块是25字节块,因此pvPortMalloc()将25字节块分割为一个20字节的块和一个5 bytes的块,然后返回一个指向20字节块的指针。新的5字节块仍然可以用于将来对pvPortMalloc()的调用。
与heap_4不同,Heap_2不会将相邻的空闲块合并为一个更大的块,因此更容易出现碎片。然而,如果分配和随后释放的块的大小总是相同,那么碎片就不是问题。Heap_2适用于重复创建和删除任务的应用程序,前提是分配给所创建任务的堆栈大小不变。
从上图可以看到Heap_2在创建、删除和再次创建任务时,最佳匹配算法如何工作的。
1. A显示创建了三个任务之后的数组。一个大的空闲块保留在数组的顶部。
2. B显示删除其中一个任务后的数组。数组顶部的大空闲块仍然存在。现在还有两个较小的空闲块,是之前分配给已删除任务的TCB和堆栈,现在被释放出来了。
3.C显示创建另一个任务后的情况。创建任务导致了对pvPortMalloc()的两个调用,一个用于分配新的TCB,另一个用于分配任务堆栈。任务是使用xTaskCreate() API函数创建的,对pvPortMalloc()的调用发生在xTaskCreate()内部。
每个TCB的大小都是完全相同的,因此最佳拟合算法确保以前分配给删除任务的TCB的RAM块被重用,以分配新任务的TCB。
堆栈的大小分配给新创建的任务是相同的,先前删除任务,分配给最适合的算法确保以前分配的内存块删除任务的堆栈分配新任务的堆栈中重用。
数组顶部较大的未分配块保持不变。
Heap_2不是确定的,但是它比malloc()和free()的大多数标准库实现都要快。、
Heap_3.c
接下来我们再看Heap_3.c。
Heap_3.c使用标准库malloc()和free()函数,因此堆的大小由链接器配置定义,configTOTAL_HEAP_SIZE设置没有影响。
Heap_3通过临时挂起FreeRTOS调度程序,使malloc()和free()线程安全。线程安全和调度程序挂起在后面将会有讨论。