2.1 为什么要自己实现内存管理
为了让FreeRTOS更容易使用,内核对象一般都是动态分配:用到时分配,不使用时释放。使用内存的动态管理功能,简化了程序设计,不再需要小心翼翼地提前规划各类对象,简化API函数的涉及,甚至可以减少内存的使用。
在C语言的库函数中,有mallc、free等函数,但是在FreeRTOS中,它们不适用:
-
不适合用在资源紧缺的嵌入式系统中
-
这些函数的实现过于复杂、占据的代码空间太大
-
并非线程安全的(thread-safe)
-
运行有不确定性:每次调用这些函数时花费的时间可能都不相同
-
内存碎片化
-
使用不同的编译器时,需要进行复杂的配置
-
有时候难以调试
2.2 什么堆栈
其实栈是栈,堆是堆,没有堆栈之说,堆栈通常是指栈。
-
堆,heap,就是一块空闲的内存,需要提供管理函数
-
malloc函数作用:从堆里划出一块空间给程序使用
-
free函数作用:用完后,再把它标记为"空闲"的,可以再次使用
-
-
栈,stack,函数调用时局部变量保存在栈中,当前程序的环境也是保存在栈中
-
可以从堆中分配一块空间用作栈
-
栈 :先进后出 FILO first in last out,栈只有一个口,另外一个口是堵死的。
-
队列:先进先出 FIFO first in first out,队列有两个口,必须入口进去,从出口出来。
-
2.3 FreeRTOS的5种内存管理方法
FreeRTOS中内存管理的接口函数为:pvPortMalloc 、vPortFree,对应于C库的malloc、free。
文件在FreeRTOS/Source/portable/MemMang
下,它也是放在portable
目录下,表示你可以提供自己的函数。
文件 | 优点 | 缺点 | 应用场合 |
---|---|---|---|
heap_1.c | 分配简单,没有碎片,时间确定 | 只分配、不回收 | 适用要求非常严格的系统里,不允许使用动态内存的场景 |
heap_2.c | 动态分配、最佳匹配算法 | 碎片、时间不定 | 过时了,不推荐Heap_2。用Heap_4替代Heap_2,更高效。 |
heap_3.c | 调用标准库函数 | 速度慢、时间不定 | 适用使用标准C库中的malloc、free函数的场景 |
heap_4.c | 动态分配、相邻空闲内存可合并 | 较少碎片问题、时间不定 | 适用频繁分配、释放不同大小内存的场景 |
heap_5.c | 在heap_4基础上支持分隔的内存块 | 较少碎片问题、时间不定 | 适用内存块地址不连续的场景 |
注:通过对比可以看出一般最常用的是heap_4.c内存管理方式。
2.4 Heap相关的函数
2.4.1 pvPortMalloc 和 vPortFree
函数原型:
void * pvPortMalloc( size_t xWantedSize ); // 分配内存,如果分配内存不成功,则返回值为NULL。
void vPortFree( void * pv ); // 释放内存
作用:分配内存、释放内存。如果分配内存不成功,则返回值为NULL。
2.4.2 xPortGetFreeHeapSize
函数原型:
size_t xPortGetFreeHeapSize( void );
当前还有多少空闲内存,这函数可以用来优化内存的使用情况。比如当所有内核对象都分配好后,执行此函数返回2000,那么configTOTAL_HEAP_SIZE就可减小2000。
注意:在heap_3中无法使用。
2.4.3 xPortGetMinimumEverFreeHeapSize
函数原型:
size_t xPortGetMinimumEverFreeHeapSize( void );
返回:程序运行过程中,空闲内存容量的最小值。
注意:只有heap_4、heap_5支持此函数。
2.4.4 malloc失败的钩子函数
在pvPortMalloc函数内部:
void * pvPortMalloc( size_t xWantedSize )
{
......
#if ( configUSE_MALLOC_FAILED_HOOK == 1 )
{
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook(); //钩子函数
}
}
#endif
return pvReturn;
}
所以,如果想使用这个钩子函数:
-
在FreeRTOSConfig.h中,把configUSE_MALLOC_FAILED_HOOK定义为1
-
提供vApplicationMallocFailedHook函数
-
pvPortMalloc失败时,才会调用此函数