深度解剖~ FreeRtos阅读笔记5 FreeRtos内存管理详解

5  Freertos 内存管理

芯片中最为稀缺珍贵的每每是存储资源,为了更好的利用它们,开发者不得不变得吝啬分配、斤斤计较到每个字节。数组

FreeRtos V8.0.1针对动态内存分配提供了四种方案,分别放在heap一、heap二、heap三、heap4文件中。spa

 

5.1 分配方式  heap1

heap1分配方法最为简单,代码量也相对较小,heap1只能申请不能回收,适合任务、队列等不须要执行删除操做的工程。线程

ucHeap占据的整块内存既为可申请的堆空间,空间大小由configTOTAL_HEAP_SIZE定义。索引

分配以前首先对堆的起始地址作偶对齐,这有可能会使一些起始的字节做为“占位”而丢弃掉,最终获得堆有效空间使用configADJUSTED_HEAP_SIZE代替。队列

(堆初始化)内存

 

Heap1分配时使用xNextFreeByte始终记录堆中可分配的首地址,起始值为0。资源

若初始化后须要申请20字节:开发

(申请20字节)源码

 

第二次申请30字节:内存管理

(申请30字节)

 

 

5.2 分配方式 heap2

Heap2中每一个内存块都有对应结构体的记录链表和块大小,链表将空闲块按照块大小升序相连。Start和end结构体做为索引独立于堆区,end做为结束标识xBlockSize老是等于堆区总大小。

Heap2初始化堆区后图:

(heap2初始化后)

 

5.2.1 heap2申请内存

若是程序须要申请A、B、C三块内存,须要的空间大小为B>A>C,使用heap2的申请分配图以下:

(申请A空间)

 

(申请B、C空间)

 

5.2.2 heap2回收内存

Heap2回收时将内存块按照升序方式插入链表。假设剩余的D空间大小等于A,程序分别释放B、A、C内存,释放后堆区以下:

(释放B内存后

 

(释放A内存后

 

(释放C内存后

内存链上的内存按照块大小递增,记录块信息的结构体随着分配逐渐增长,永远得不到释放。一旦程序频繁申请小块内存,即便释放后也没法申请大内存块。

 

5.3 分配方式 heap3

Heap3使用了库中的malloc和free,此时堆区已经脱离freertos控制,configTOTAL_HEAP_SIZE成为无效参数。没有源码,,,,先不作分析~

 

5.4 分配方式 heap4

Heap4中每一个内存块都有对应结构体的记录链表和块大小,链表将空闲块按照地址升序相连。Start结构体做为索引独立于堆区,end属于堆区一部分,因此去除一个结构体的size才是堆实际分配范围。

(heap4初始化后的堆区)

 

5.4.1 heap4申请内存

每当申请出空闲块后都会对应产生一个新的结构体,这些结构体会占据一部分堆空间。申请内存块A后堆区图:

(申请A内存后)

 

再次申请B、C内存块:

(申请B内存后)

 

(申请C内存后)

 

5.4.2 heap4 回收内存

heap4在回收内存时会尝试将零散的内存块进行合并,减小碎片。

假设在申请A、B、C以后,B内存使用完毕须要释放掉,B块的上下都没有相邻的空闲内存,因此只插入空闲链表就能够完成释放。如图:

(释放B后)

 

释放A时,存在相邻的B区,两块内存会进行拼接,B块的结构体也被合并,如图:绿色区是被回收的内存

(释放A后)

 

回收C块时,上下都存在空闲区,回收工做分为两个步骤进行:

(释放C

 

5.5 阅读小结

相同点

除heap3外,都使用大数组做为堆区进行分配,这看起来比较浪费,即便程序没有申请任何空间,堆区占据的内存在程序编译前就已经肯定。

Heap2与heap4都须要为记录内存块信息而消耗必定堆空间。

调用相同,统一使用pvPortMalloc和vPortFree,方便heap文件更换。

进行堆操做时都没有屏蔽中断,只是将调度器挂起。因此FreeRtos堆操做不能在中断里使用,若是必定要实现的话,线程中每个堆操做的地方如建立任务、信号量、队列等~都要先屏蔽中断,这会使FreeRtos调度器实时性降低。

 

区别

四种方式主要在分配方法上存在差别:

Heap1分配最为简单迅速,它适合只申请不释放的工程。

Heap2进行频繁申请释放后会形成较多的碎片,适合只申请固定大小内存的工程。

Heap4像是Heap2的升级版,分配方式几乎相同,在回收时添加了内存块拼接以尝试消除碎片。

相关文章
相关标签/搜索