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

5  Freertos 内存管理

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

FreeRtos V8.0.1针对动态内存分配提供了四种方案,分别放在heap1、heap2、heap3、heap4文件中。

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的升级版,分配方式几乎相同,在回收时添加了内存块拼接以尝试消除碎片。

猜你喜欢

转载自my.oschina.net/u/3699634/blog/1550962
今日推荐