当FreeRTOS每次创建任务,队列,互斥,软件定时器,信号量时分配相关的RAM。标准C函数库的malloc()和free()虽然有些情况下能用于分配内存与释放内存,但是它们却有如下的缺点:
1.在嵌入式系统中它们并不是都是有效的
2.它们占据了有效的代码空间
3.它们是不安全的线程
4.它们的执行时间是不确定的
...因此为了解决这个问题需要另一种分配内存的方法被使用!
嵌入式系统与实时系统有很多种不同规格的RAM和时间要求。因此往往单一的内存分配算法只能适用于单一的应用程序。
FreeRTOS为解决这个问题,当RTOS内核需要RAM时使用的是pvPortMalloc()而不是malloc(),当RTOS内核释放内存时使用的是pvPortFree()而不是free()。
FreeRTOS的源文件中有四个内存分配文件heap_1.c,heap_2.c,heap_3.c,heap_4.c。如有需要,其它的内存分配方式算法文件可以被添加。在一个工程中上面的四个文件只能使用一个文件。
Heap_1.c
这种方式的原理是将一个大的内存块按照每次的内存分配需求从里面划分出来。通过查看heap_1.c的源代码可知,分配内存前需要将要分配的大小按照微处理器的架构进行对齐,如16bit单片机是按双字节对齐的,之后还要确定内存的起始地址是边界对齐的(具体操作是对内存地址的操作)。同时每次分配前还要和空闲的内存大小作比较,如若有误可运行用户定义的钩子函数。在这种方式下每次内存分配的时间都是相同的,但是这种方式不能将已分配出去的内存进行回收。因为在这种方式下,内存分配的数据结构只是一个简单的数组,从原理上就不能提供这些灵活的功能,在Heap_2.c和Heap_4.c中采用的是链式算法,相比数组数据结构而言就具有了很高的灵活性。
Heap_3.c
Heap_3.c没有什么特别之处,就是对编译器本身的malloc()和free()函数进行了重包装(simple wrapper)。这个包装只是简单的让malloc()和free()变得安全些(通过查看代码发现其实并未对这两个函数做很多的包装,所以估计这两个内存分配函数的线程也并不是很安全)。这种方式要求如下几点:
1.要求链接器自己建立内存空间(Heap),并且编译器要提供标准的malloc()与free()函数
2.由于是使用简单的malloc与free函数,所以执行时间同样也是不确定的
很有可能的会增加FreeRTOS的内核代码,所以在内存极其有限的硬件平台上就不能很好的使用。
Heap_2.c
Heap_2.c使用了最匹配的算法,它允许被分配的内存能释放。它使用了单链表的数据结构,初始下内存的大小为可配置的最大值,然后在一步步划分出去。同样它也是从一个大的内存块中根据需要的内存大小而从里面划分出来的。但是它却不具有将连续相邻的空闲块联合成一个大块,而是采用将空闲块按照由小到大的顺序排列起来(排列的地方依旧在原来的的内存块中),所以会让原来本是相邻的块变得不相邻,这就将造成内存碎片(即打破了原来的内存结构,有的部分是存了数据而有的部分又没有存数据)换句话说也就是它不具有"合并(coalescence)"算法。这种方式具有如下特点:
即使当应用程序不断的删除任务,队列,信号,互斥等等也能正常使用
当分配内存与释放内存的大小是随机的这种方式是一定不能使用的。
1.如果应用程序不断的动态创建或删除任务,但是每次改变的内存大小都是相同的,那么这种方式是可以使用的,因为由于每次分配的大小都是相同的,即使释放后按照由小到大的顺序来排列也是按原来的结构组织的,并且由于分配的内存大小是相同的不会分到下个区段里去。然而如果每次分配的内存大小不相同,那么一个大块将会被打断成许多的小块,造成了大量的内存碎片,然后如果还继续分配内存的话,就很有可能引起分配错误导致程序运行异常(比如,分配的内存大小超过了此空闲区段的大小而只能延伸到下个区段里去,但是极有可能下个区段已经被使用了)。同理,队列的分配与释放也如此!
2.如果应用程序是直接的调用pvPortMalloc()与pvPortFree()函数,而不是通过其它的API函数调用,在这种情况下是不能使用这种方式的。
2.1如果你的应用程序中的任务,队列,信号,互斥的顺序是不可预知的,那么就有可能出现内存碎片的情况。当然这种情况发生的概率会很低,但不可掉以轻心!
2.2每次的执行时间是不确定的(因为使用了链式结构),但是相比C库里的内存分配函数malloc与free却是具有高效率的。
Heap_2.c是适合于很多小型的实时系统的,这种实时系统往往会动态的创建与删除任务。
Heap_4.c
Heap_4.c也使用了最匹配的算法,它和Heap_2.c基本上是相似的,唯一不同的就是这种算法支持空闲块联合,这样就避免了内存碎片的出现。这种算法具有如下特点:
1.即使当应用程序不断的创建或删除任务,信号,队列等等情况下,也是能正常使用的
2.不会像Heap_2.c那样会产生大量的碎片,但在某些情况下也是会产生内存碎片的。
3.同样每次的执行时间也是不确定的
这种方式是针对想在应用程序中直接使用可移植层来分配内存的(可能翻译的不是很清楚,等以后具体应用在总结)。
FreeRTOS的代码编写风格与思路还是比较清晰的,可以读读原代码,值得我们学习与借鉴!
文章评论(0条评论)
登录后参与讨论