FreeRTOS内存管理

简介

Freertos的内存管理分别在heap_1.c,heap_2.c,heap_3.c,heap_4.c,heap_5.c个文件中,选择合适的一种应用于嵌入式项目中即可。

本文的图片中
红色部分Block代表:在内存对齐过程中舍弃掉的部分字节。
蓝色部分Block代表:链表结构体头,包含可以分配的内存大小和Next指针。
绿色部分Block代表:实际可分配给用户的内存。
黄色部分Block代表:已经分配给用户的内存。


heap_1.c

特点:

  1. 最简单的内存分配算法。
  2. 分配后无法释放内存。
  3. 支持1/2/4/8/16/32多种内存对齐方式。
  4. 线程安全。

初始化

初始化

  • ucHeadp数组代表整个堆内存空间。
  • xNextFreeByte = 0。
  • 初始化以后pucAlignedHeap指针会指向内存对齐后的第一个位置。

*pvPortMalloc():

  • 依次返回pvReturn(pucAlignedHeap+xNextFreeByte)所指向的地址,并更新xNextFreeByte大小。


### vPortFree():

  • 无法使用,传入的指针非空会进入断言。

heap_2.c

特点:

  1. 采用链表结构管理,按内存块大小排序。
  2. 分配后可以释放内存,但是会产生内存碎片。
  3. 支持1/2/4/8/16/32多种内存对齐方式。
  4. 线程安全。

初始化

heap2_Init

  • ucHeap数组代表整个堆内存空间。
  • 初始化以后pucAlignedHeap指针会指向内存对齐后的第一个位置。
  • xStart表示链表头,pxFirstFreeBlock代表第一块可分配空间,xEnd代表链表尾。

*pvPortMalloc():

heap2_malloc

  • 按内存字节对齐方式调整实际分配大小。(实际大小=内存对齐+链表结点结构体大小)
  • 遍历整个内存链表,如果找到足够分配的内存Block头,则加上结构体头偏移量,将pvReturn位置记录待返回给用户,并将该结点从链表中移除。
  • 分配完成后,如果剩余的内存大小仍然大于最小的BlockSize(包含链表结构体),就将该Block切割,并按照BlockSize从小到大的方式插入空闲内存链表。


###vPortFree():

heap2_free

  • Free的过程非常简单,只是根据传入的指针参数,反向找到结构体头,然后根据该节点BlockSize从小到大重新插入空闲内存链表中即可。

总结:

  1. 内存的分配和释放过程是线程安全的。
  2. 在内存分配和释放过程会产生内存碎片,所以heap_2.c不适用于频繁地内存分配的场合。试想这样一种情况:不停地进行小片内存分配(比如:8个字节)直到内存用完,分配完成后全部重新Free,由于结点信息仍然存在,将导致以后都无法分配超过8个字节的内存。

heap_3.c

特点:

  1. 使用标准库中的malloc和free方法。
  2. 无法在中断中分配和释放内存。
  3. 线程安全。

在标准库的基础上,简单地加上线程安全的处理,保证malloc不会被线程切换打断。


heap_4.c

特点:

  1. 采用链表结构管理,按地址大小排序。
  2. 分配后可以释放内存,不会产生内存碎片。
  3. 支持1/2/4/8/16/32多种内存对齐方式。
  4. 线程安全。

初始化

heap4_init

  • ucHeadp数组代表整个堆内存空间。
  • 初始化以后pucAlignedHeap指针会指向内存对齐后的第一个位置。
  • xStart表示链表头,pxFirstFreeBlock代表第一块可分配空间,xEnd代表链表尾。和heap_2.c不一致的是这里的链尾结构体也分配在ucHeap内存中。

*pvPortMalloc():

heap4_malloc

  • 按内存字节对齐方式调整实际分配大小。(实际大小=内存对齐+链表结点结构体大小)
  • 遍历整个内存链表,如果找到足够分配的内存Block头,则加上结构体头偏移量,将pvReturn位置记录待返回给用户,并将该结点从链表中移除。
  • 如果剩余的内存大小仍然大于最小的BlockSize(包含链表结构体),就将Block切割,并按照地址从小到大的方式插入内存链表,并更新xFreeBytesRemaining。


###vPortFree():

heap4_free

  • 根据传入的指针参数,反向找到结构体头,然后迭代空闲链表,根据该节点的地址从低到高,找到相应的插入位置。
  • 当Free的时候,有两种情况,一种是连续(如图中黄色块3),一种是不连续(如图中黄色块1,2)。
  • 插入空闲内存链表后,如果该节点与后面的节点是连续的,则将这两个节点整合成一个大的节点。

总结:

  1. 内存的分配和释放过程是线程安全的。
  2. 和heap_2.c非常相似,最大不同点在于释放内存的时候不会产生碎片,具体做法是在插入空闲内存链表的时候多了一步操作:如果两个空闲节点在内存上是连续的,则将这两个节点重新整合成一个。
  3. 
另外一点和heap_2.c不同的地方在于,空闲内存链表排序方式不一样,heap_2.c是按照blockSize排序,而heap_4.c是按照内存地址排序,所以heap_4.c的空闲链表在内存上是很容易整合的,而heap_2.c由于没有按内存地址排序,所以整合比较麻烦。

heap_5.c

特点:

  1. 采用链表结构管理,按地址大小排序。
  2. 分配后可以释放内存,不会产生内存碎片。
  3. 支持1/2/4/8/16/32多种内存对齐方式。
  4. 线程安全。
  5. 可以自定义内存区。

Malloc和Free方法和heap_4.c一模一样。
区别在与heap_4.c使用prvHeapInit()函数将ucHeap这个全局变量作为内存区,而heap_5.c没有这个prvHeapInit()函数,而是提供了另一个函数vPortDefineHeapRegions(),自己定义内存区。

猜你喜欢

转载自www.cnblogs.com/xw-kaka/p/9329260.html
今日推荐