在计算系统中,变量、中间数据一般存放在系统存储空间中,只有在实际使用时才将 它们从存储空间调入到中央处理器内部进行运算。通常存储空间可以分为两种:内部存储 空间和外部存储空间。内部存储空间访问速度比较快,能够按照变量地址随机地访问,也 就是我们通常所说的RAM(随机存储器),或电脑的内存;而外部存储空间内所保存的内 容相对来说比较固定,即使掉电后数据也不会丢失,可以把它理解为电脑的硬盘。在这一 章中我们主要讨论内部存储空间(RAM)的管理——内存管理。
内存分配时需要的总的堆空间由文件FreeRTOSConfig.h 中 的宏 configTOTAL_HEAP_SIZE 配置,单位为字。(定义系统的总内存大小)。
在嵌入式程序设计中内存分配应该是根据所设计系统的特点来决定选择使用动态内存 分配还是静态内存分配算法,一些可靠性要求非常高的系统应选择使用静态的,而普通的 业务系统可以使用动态来提高内存使用效率。静态可以保证设备的可靠性但是需要考虑内 存上限,内存使用效率低,而动态则是相反。
FreeRTOS内存管理模块管理用于系统中内存资源,它是操作系统的核心模块之一。主 要包括内存的初始化、分配以及释放。
在一般的实时嵌入式系统中,由于实时性的要求,很少使用虚拟内存机制。所有的内 存都需要用户参与分配,直接操作物理内存,所分配的内存不能超过系统的物理内存,所有的系统堆栈的管理,都由用户自己管理。
版本
FreeRTOS的 V9.0.0版本为我们提供了 5种内存 管理算法,分别是 heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c。FreeRTOS的内存管理模块通过对内存的申请、释放操作,来管理用户和系统对内存的 使用,使内存的利用率和使用效率达到最优,同时最大限度地解决系统可能产生的内存碎 片问题。
在野火的例程中使用的是heap_4.c方案。
该方案集成了最佳匹配算法、碎片回收,也是管理一个configTOTAL_HEAP_SIZE大小的数组;可随机调用pvPortMalloc()和vPortFree()来申请-释放任意大小空间,且不会产生内存碎片。
该方案同样需要面对安全问题:需要管理configTOTAL_HEAP_SIZE的大小,否则就会有内存申请失败发生;申请内存时间存在不确定性。
各方案对比
相关函数
内存申请函数pvPortMalloc()
是从链 表头 xStart 开始遍历查找合适的内存块,如果某个空闲内存块的大小能容得下用户要申请 的内存,则将这块内存取出用户需要内存空间大小的部分返回给用户,剩下的内存块组成 一个新的空闲块,按照空闲内存块起始地址大小顺序插入到空闲块链表中,内存地址小的 在前,内存地址大的在后。在插入到空闲内存块链表的过程中,系统还会执行合并算法将 地址相邻的内存块进行合并:判断这个空闲内存块是相邻的空闲内存块合并成一个大内存 块,如果可以则合并,合并算法是 heap_4.c内存管理方案和 heap_2.c内存管理方案最大的 不同之处,这样一来,会导致的内存碎片就会大大减少,内存管理方案适用性就很强,能 一样随机申请和释放内存的应用中,灵活性得到大大的提高
内存释放函数vPortFree()
heap_4.c内存管理方案的内存释放函数vPortFree()也比较简单,根据传入要释放的内 存块地址,偏移之后找到链表节点,然后将这个内存块插入到空闲内存块链表中,在内存 块插入过程中会执行合并算法,这个我们已经在内存申请中讲过了(而且合并算法多用于 释放内存中)。最后是将这个内存块标志为“空闲”(内存块节点的xBlockSize成员变量 最高位清0)、再更新未分配的内存堆大小即可。
xPortGetFreeHeapSize()
通过调用函数 xPortGetFreeHeapSize() 我们可 以知道还剩下多少内存没有使用,但是并不包括内存碎片。这样一来我们可以实时的调整 和优化configTOTAL_HEAP_SIZE的大小。
uxTaskGetStackHighWaterMark( xTaskHandle xTask )
返回从任务启动栈空间具有的最小剩余量这个值越是接近 0,说明这个任务快溢出了。判断任务所需空间及栈溢出问题。
可以用来检测任务启动所需最小剩余栈空间大小,然后我们就可以计算出最大使用的大小,一般可以再乘以1.5左右作为最终分配的值。
比如任务一分配了512个内存,函数返回数值为400,则可知任务最大使用了112个内存。112*2=224,可把224替换512成为任务一分配的内存大小从而节约系统内存空间。
//内存管理任务,查询最小剩余栈空间大小,来分配任务堆栈空间大小。按下按键1获取系统当前剩余内存
void Test_Task(void* parameter)
{
//打印出来该任务自启动起来最小剩余栈空间大小,然后我们就可以计算出最大使用的大小,一般可以再乘以1.5左右作为最终分配的值。
// printf(" the min free stack size is %d \r\n",(int32_t)uxTaskGetStackHighWaterMark(Test_Task_Handle));
uint32_t g_memsize;
while (1)
{
if( Key_Scan(KEY1_GPIO_PORT,KEY1_PIN) == KEY_ON )
{
/* 获取当前内存大小 */
g_memsize = xPortGetFreeHeapSize();
printf("系统当前内存大小为 %d 字节\n",g_memsize);
}
vTaskDelay(20);/* 延时20个tick */
}
}