内存管理
每当任务,队列或是信号量被创建时,内核需要进行动态内存分配。虽然可以调用标准的 malloc()与 free()库函数,但必须承担以下若干问题:
- 这两个函数在小型嵌入式系统中可能不可用。
- 这两个函数的具体实现可能会相对较大,会占用较多宝贵的代码空间。
- 这两个函数通常不具备线程安全特性。
- 这两个函数具有不确定性。每次调用时的时间开销都可能不同。
- 这两个函数会产生内存碎片。
- 这两个函数会使得链接器配置得复杂
不同的嵌入式系统具有不同的内存配置和时间要求。所以单一的内存分配算法只可能适合部分应用程序。因此,FreeRTOS 将内存分配作为可移植层面(相对于基本的内核代码部分而言)。
在小型嵌入式系统中, 通常是在启动调度器之前创建任务,队列和信号量。这种情况表明,动态分配内存只会出现在应用程序真正开始执行实时功能之前,而且内存一旦分配就不会再释放。 这就意味着选择内存分配方案时不必考虑一些复杂的因素,比如确定性与内存碎片等,而只需要从性能上考虑,比如代码大小和简易性。
内存分配方案范例
Heap_1.c
Heap_1.c 实现了一个非常基本的 pvPortMalloc()版本,而且没有实现 vPortFree()。如果应用程序不需要删除任务,队列或者信号量,则具有使用 heap_1 的潜质。Heap_1总是具有确定性
这种分配方案是将 FreeRTOS 的内存堆空间看作一个简单的数组。当调用pvPortMalloc()时,则将数组又简单地细分为更小的内存块。
数组的总大小(字节为单位)在FreeRTOSConfig.h中由configTOTAL_HEAP_SIZE定义。
需要为每个创建的任务在堆空间上分配一个任务控制块(TCB)和一个栈空间。下图展示了 heap_1 是如何在任务创建时细分这个简单数组的。
- A 表示数组在没有任何任务创建时的情形,这里整个数据是空的。
- B 表示数组在创建了一个任务后的情形。
- C 表示数组在创建了三个任务后的情形。
Heap_2.c
Heap_2.c 也是使用了一个由 configTOTAL_HEAP_SIZE 定义大小的简单数组。不同于 heap_1 的是,heap_2 采用了一个最佳匹配算法来分配内存,并且支持内存释放。由于声明了一个静态数组,所以会让整个应用程序看起来耗费了许多内存——即使是在数组没有进行任何实际分配之前。
最佳匹配算法保证 pvPortMalloc()会使用最接近请求大小的空闲内存块。比如,考虑以下情形:
- 堆空间中包含了三个空闲内存块,分别为 5 字节,25 字节和 100 字节大小。
- pvPortMalloc()被调用以请求分配 20 字节大小的内存空间。
Heap_2.c 并不会把相邻的空闲块合并成一个更大的内存块,所以会产生内存碎片——如果分配和释放的总是相同大小的内存块,则内存碎片就不会成为一个问题。Heap_2.c 适合用于那些重复创建与删除具有相同栈空间任务的应用程序。
A 表示数组在创建了三个任务后的情形。数组的顶部还剩余一个大空闲块。
B 表示数组在删除了一个任务后的情形。顶部的大空闲块保持不变,并多出了两
个小的空闲块,分别是被删除任务的 TCB 和任务栈。
C 表示数组在又创建了一个任务后的情形。创建一个任务会产生两次调用
pvPortMalloc(),一次是分配 TCB,一次是分配任务栈(调用 pvPortMalloc()发生在 xTaskCreate() API 函数内部)。
每个 TCB 都具有相同大小,所以最佳匹配算法可以确保之前被删除的任务占用的 TCB 空间被重新分配用作新任务的 TCB 空间。
新建任务的栈空间与之前被删除任务的栈空间大小相同,所以最佳匹配算法会保证之前被删除任务占用的栈空间会被重新分配用作新任务的栈空间。 数组顶部的大空闲块依然保持不变。
Heap_2.c 虽然不具备确定性,但是比大多数标准库实现的 malloc()与 free()更有效率。
Heap_3.c
Heap_3.c 简单地调用了标准库函数 malloc()和 free(),但是通过暂时挂起调度器使得函数调用备线程安全特性。
此时的内存堆空间大小不受 configTOTAL_HEAP_SIZE 影响,而是由链接器配置决定。
void *pvPortMalloc( size_t xWantedSize )
{
void *pvReturn;
vTaskSuspendAll();
{
pvReturn = malloc( xWantedSize );
}
xTaskResumeAll();
return pvReturn;
}
void vPortFree( void *pv )
{
if( pv != NULL )
{
vTaskSuspendAll();
{
free( pv );
}
xTaskResumeAll();
}
}