动态内存:可靠性低,但是内存使用效率高
静态内存:可靠性高,但是内存使用效率低
C标准库是提供了内存分配和内存释放的库函数malloc()和free();但是,在嵌入式系统中,是没有直接进行使用,原因如下
在小型嵌入式系统中并不总是可用的,小型嵌入式设备的RAM不足
占据了相当大的代码空间
在使用的过程中,可能产生碎片
不安全,这个不安全是指:内存的分配和释放的问题
一般的内存管理算法是:是根据需要存储的数据长度在内存中寻找和这一段数据相适应的的空闲内存块,然后将数据存储在里面。这个寻找的时间是不确定的,这对于实时操作系统来说,是不可取的。
在FreeRTOS中一共提供了5种的内存管理
这五种的管理方式,各有侧重点,可以按需选择。对于heap_1.c、heap_2.c、heap_4.c, 这三个实际上是一个很大的数组(static u8 ucHeap[config_TOTAL_HEAP_SIZE]),这样只要知道了分配的数组(内存)的大小,然后通过它作为偏移量,就可以知道未分配的内存地址;对于heap_3.c这个是对C库的Malloc方法和Free方法的再封装;而对于heap_5.c这个则是用来针对自身RAM不够用的时候,扩展了外部的SRAM的时候使用,它可以允许用户使用多个非连续的堆空间,每个堆空间的起始地址和大小可以由用户自己定义,常见的就是应用于:图形显示(GUI)
heap_1.c:只分配,不释放
应用:用于从不删除任务、队列、信号量等内核对象的应用程序(绝大多数的应用程序都是这样)
实现方法:主要还是看两个变量,一个是xNextFreeByte,一个是*pucAlignedHeap 这两个变量对内存的分配进行跟踪
xNextFreeByte:这个是指向下个空闲内存块的
*pucAlignedHeap:这个则是指向了对齐后内存地址的。(内存对齐:大多数硬件对内存对齐的数据进行访问的时候,更快速。比如一个4字节的数据,存在地址为0x0001上,然后系统是从0x0000开始读取数据的,每次4个字节,然后这样,就会有一个问题,那就是一次是读不完其实地址为0x0001的数据,只能读到0x0000~0x0003,少了一个字节,内存对齐之后,数据是从0x0000开始存的,那就可以一次性的读走数据)
heap_2.c:既分配又释放
采取了最佳匹配算法进行内存的分配,使得系统能过找到最适合当前数据最适合的储存的内存块,主要是为了防止内存的浪费,比如说有一个100字节的数据,要存储,剩余的内存块是200字节、300字节和400字节,那么系统就回将200字节的内存空间进行切割。
应用:适合用于那些需要反复删除任务、队列、信号量等内核对象的,而且还不担心生成内存碎片的。或者是那些(例如内核对象)每次申请的内存大小固定的,这样在申请之后,在释放,也是可以被其他对象进行使用的,不一样大的就会造成碎片化(小的并不能给大的用,虽然内存空间还是足够的)。
实现方法:利用数据结构---链表,将空闲的内存块添加的链表中,以升序进行排列,其中涉及了4个变量:xStart就是链表的头,xEND就是链表的尾,然后就是内存块中包含的成员变量*pxNextFreeBlock(指向了下一个空闲的内存块)和xBlockSize(内存块的大小);一般都是在切割后的内存头部添加上一个内存结点,并且完善这个内存块,然后添加到链表中。
heap_3.c:对Malloc和Free方法只是进行了再次封装
再次封装之后,使得Malloc和Free方法具有安全保障,也就是具有保护功能
特点:
需要链接器设置一个堆,malloc和free方法由编译器提供,
具有不确定性
很有可能增大RTOS的内核大小
注意事项:在使用这个内存管理的时候,需要在启动文件startup_stm32f10x_md.s文件中的第45行的:Heap_Size EQU 0x00000200 进行设置堆大小;此时的FreeRTOSConfig.h文件中的configTOTA_HEAP_SIZE的宏定义不起作用!!!!
heap_4.c:既分配又是释放,还可以合并相邻的空闲内存块
可以说,这个是在heap_2.c的基础上,添加了一个功能:内存合并
应用:可用于重复删除的任务、队列、信号量等内核对象的应用程序,可以用于分配和释放随机字节内存的的应用程序,但是并没有产生像heap_2.c那样严重的内存碎片
实现方法:这个也是利用了链表,但是,这个和heap_2.c不一样的地方是,按照地址的升序进行排列,就是为了适配内存合并这个算法,当在内存释放的时候,内存块,会对它前一个和后一个内存块,进行地址判断,如果是当前的内存块的地址尾是后一个内存块的起始地址,那么就会合并成一个更大的内存块(同理,如果当前内存块的地址头是前一个内存块的地址尾,那么也会合并成一个更大的内存块)(内存块的成员变量:*pxNextFreeBlock(指向了下一个空闲的内存块)和xBlockSize(内存块的大小))。

heap_5.c:跨区进行内存操作
这个算是在heap_4.c的基础上,再次升级,多了一个新的算法,那就跨区操作,允许跨多个非连续的内存区进行操作,例如在片内RAM定义了一个内存堆,然后再在外部定义了一个内存堆,都是由系统进行管理。
应用:常见的就是在内部RAM不足的时候,才需要用这个内存管理方案,典型就是应用于图形化界面
实现方法:通过HeapReagion_t类型的结构体,来定义内存的其实地址和大小,然后通过对vPortDefineHeapRegions()函数初始化,形参就是HeapReagion_t类型的数组, 这个数组必须用一个 NULL 指针和 0 作为结尾,起始地址必须从小到大排列。 而且必须要先进行内存的初始化,因为像内核兑现的创建和使用,都会隐式调用vPortMalloc这个内存分配函数,所以需要先进行内存的初始化。

初始化完成后,内存地址如下所示:

实现方法:就是在进行内存分配的时候先挂起调度器,完成之后,再恢复调度器