1.前言
不同的嵌入式系统具有不同的内存配置和时间要求。所以单一的内存分配算法只可能适合部分应用程序。
FreeRTOS 将内存分配作为可移植层面(相对于基本的内核代码部分而言)。这使得不同的应用程序可以提供适合自身的具体实现。
本章期望让读者了解以下事情:
- FreeRTOS 在什么时候分配内存。
- FreeRTOS 提供的三种内存分配方案范例
2.FreeRTOS内存管理概述
(1)当内核请求内存时,其调用pvPortMalloc()而不是直接调用malloc();当释放内存时,调用vPortFree()而不是直接调用free()。
pvPortMalloc()具有与malloc()相同的函数原型;vPortFree()也具有与free()相同的函数原型。
(2)FreeRTOS 自带有三种pvPortMalloc()与vPortFree()实现范例.FreeRTOS 的用户可以选用其中一种,也可以采用自己的内存管理方式
这三个范例对应三个源文件:heap_1.c,heap_2.c,heap_3.c——这三个文件都放在目录FreeRTOSSourcePortableMemMang 中
(3)早期版本的FreeRTOS 所采用的原始内存池和内存块分配方案已经被移除了,因为定义内存块和内存池的大小需要较深入的努力和理解。
(4)选择内存分配方案时不必考虑一些复杂的因素,比如确定性与内存碎片等,而只需要从性能上考虑,比如代码大小和简易性
在小型嵌入式系统中,通常是在启动调度器之前创建任务,队列和信号量。
这种情况表明,动态分配内存只会出现在应用程序真正开始执行实时功能之前,而且内存一旦分配就不会再释放
3. 内存分配方案范例
3.1 heap_1.c原理说明
(1)Heap_1.c 实现了一个非常基本的pvPortMalloc()版本,而且没有实现vPortFree()
如果应用程序不需要删除任务,队列或者信号量,则具有使用heap_1 的潜质
(2)这种分配方案是将FreeRTOS 的内存堆空间看作一个简单的数组。
当调用pvPortMalloc()时,则将数组又简单地细分为更小的内存块。
(3)数组的总大小(字节为单位)在FreeRTOSConfig.h 中由configTOTAL_HEAP_SIZE定义。
以这种方式定义一个巨型数组会让整个应用程序看起来耗费了许多内存——即使是在数组没有进行任何实际分配之前。
(4)需要为每个创建的任务在堆空间上分配一个任务控制块(TCB)和一个栈空间
3.2 heap_1.c内存分配示例
(1)A 表示数组在没有任何任务创建时的情形,这里整个数据是空的。
(2)B 表示数组在创建了一个任务后的情形。
(3)C 表示数组在创建了三个任务后的情形。
3.3 heap_2.c原理说明
(1)Heap_2.c 也是使用了一个由configTOTAL_HEAP_SIZE 定义大小的简单数组
(2)不同于heap_1 的是,heap_2 采用了一个最佳匹配算法来分配内存,并且支持内存释放
(3)由于声明了一个静态数组,所以会让整个应用程序看起来耗费了许多内存——即使是在数组没有进行任何实际分配之前
(4)最佳匹配算法保证pvPortMalloc()会使用最接近请求大小的空闲内存块
举例:堆空间中包含了三个空闲内存块,分别为5 字节,25 字节和100 字节大小。pvPortMalloc()被调用以请求分配20 字节大小的内存空间。
匹配请求字节数的最小空闲内存块是具有25字节大小的内存块——所以pvPortMalloc()会将这个25 字节块再分为一个20 字节块和一个5 字节块3,然后返回一个指向20 字节块的指针。
剩下的5 字节块则保留下来,留待以后调用pvPortMalloc()时使用
(5)Heap_2.c 并不会把相邻的空闲块合并成一个更大的内存块,所以会产生内存碎片——如果分配和释放的总是相同大小的内存块,则内存碎片就不会成为一个问题
(6)Heap_2.c 适合用于那些重复创建与删除具有相同栈空间任务的应用程序
3.4 heap_2.c内存分配示例
上图展示了当任务创建,删除以及再创建过程中,最佳匹配算法是如何工作的
(1)A 表示数组在创建了三个任务后的情形。数组的顶部还剩余一个大空闲块。
(2) B 表示数组在删除了一个任务后的情形。顶部的大空闲块保持不变,并多出了两个小的空闲块,分别是被删除任务的TCB 和任务栈。
(3) C 表示数组在又创建了一个任务后的情形。创建一个任务会产生两次调用pvPortMalloc(),一次是分配TCB,一次是分配任务栈
注:(1)调用pvPortMalloc()发生在xTaskCreate() API 函数内部
(2)每个TCB 都具有相同大小,所以最佳匹配算法可以确保之前被删除的任务占用的TCB 空间被重新分配用作新任务的TCB 空间。
(3)新建任务的栈空间与之前被删除任务的栈空间大小相同,所以最佳匹配算法会保证之前被删除任务占用的栈空间会被重新分配用作新任务的栈空间。数组顶部的大空闲块依然保持不变。
(4)Heap_2.c 虽然不具备确定性,但是比大多数标准库实现的malloc()与free()更有效率
3.5 heap_3.c原理说明
(1)Heap_3.c 简单地调用了标准库函数malloc()和free(),但是通过暂时挂起调度器使得函数调用具备线程安全特性
(2)内存堆空间大小不受configTOTAL_HEAP_SIZE 影响,而是由链接器配置
3.6 heap_3.c内存分配举例
4.参考文献
[1]FreeRTOS中文实用教程