【FreeRTOS】内存管理

FreeRTOS之内存管理

既然标准C库中的Malloc()与Free()也可以实现内存动态管理,为何FreeRTOS还要实现一套内存管理机制?原因如下:

  • 在小型的嵌入式系统中效率不高。
  • 会占用很多的代码空间。
  • 它们不是线程安全的。
  • 具有不确定性,每次执行的时间不同。
  • 会导致内存碎片。
  • 使链接器的配置变得复杂。

0. 【五种heap的特点】

  • heap_1: 只申请不释放,适用于一旦创建好任务、信号量、队列就再也不会删除的应用。
  • heap_2: 使用内存块相关结构体管理内存,可以释放,但是申请内存不固定的话会产生内存碎片。
  • heap_3: 使用标准C库中的Malloc()与Free()。
  • heap_4: 在heap_2的基础上增加了内存合并功能,解决了内存碎片的问题。
  • heap_5: 在heap_4的基础上,还支持管理多段不连续的内存,并将这些内存用链表连接起来。它更适用于芯片外加RAM的情况。

1. 【heap_1】

1.1 [heap_1的特性]

  • heap_1只有内存申请没有内存释放,适用于一旦创建好任务、信号量、队列就再也不会删除的应用,实际上大多数FreeRTOS应用都是这样的。
  • 具有确定性(执行所花费的时间大多数都是一样的),而且不会导致内存碎片。
  • 代码实现和内存分配过程都很简单,内存是从一个静态数组中分配的,适用于不需要动态分配内存的应用。

1.2 [heap从哪个地址开始呢?]

  • 在heap_1.c中,static uint8_t ucHeap[configTOTAL_HEAP_SIZE]分配了一个数组给堆,但是这个地址从哪里开始呢?这是由编译器(比如GCC)决定的。
  • 这个堆的首地址不一定是以8字节对齐,要想以8字节对齐需要使用gcc的__attribute__机制。

1.3 [__attribute__机制]

具体使用方法点击移步至

1.3.1 [什么是__attribute__?]

__attribute__机制是GCC的一大特色,与编译器相关。此机制可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。

1.3.2 [__attribute__语法格式]
    __attribute__ ((attribute-list))
1.3.3 [attribute_-list]
  • 数据声明
    • packed:__attribute__ ((packed))的作用是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,是GCC特有的语法。
    • aligned:__attribute__ ((aligned(n)))指定内存n字节对齐,n为任意整数。
  • 函数声明
    • __attribute__ (((noreturn))告诉编译器此函数不会返回给调用者,以便编译器在优化时去掉不必要的函数返回代码。
    • __attribute__ ((weak))将函数转为虚函数,弱符号,告诉编译器如果有定义同名的函数强符号函数则使用同名函数,否则使用此函数,用在防止某必要函数未被定义的情况。注意:weak属性只会在静态库(.o .a)中生效,动态库(.so)中不会生效。

1.4 [pvPortMalloc()申请内存流程]

  • 检查是否字节对齐,FreeRTOS默认8字节对齐。
    • 若对齐直接进行下一步
    • 若不对齐,将字节补齐(可以被8整除),申请的字节大小只能大于传进来的值。
  • 根据内存堆ucHeap计算出以8字节对齐的可用起始地址pucAlignedHeap
  • 检查内存够不够分配想要的大小,puAlignedHeap可用地址起始位置,
    xNextFreeByte可用位置减去可用起始位置的偏移量;xWantedSize想要分配的字节大小。xNextFreeByte+xWantedSize<configADJUSTED_HEAP_SIZE则不会溢出,可分配。同时更新xNextFreeByte
  • 返回申请到的内存首地址。

GNU C 的一大特色就是__attribute__

2. 【heap_2】

2.1 [heap_2的特性]

  • 适用于可能会重复的删除任务、队列、信号量等的应用中,要注意有内存碎片产生!
  • 有内存块结构;与heap_1相比将内存分为内存块用链表链接接起来实现内存的分配和释放,而heap_1就是一整片数组。
  • 如果分配和释放的内存n大小是随机的,不匹配的,那么就要慎重使用。此种情况使用heap_4是最好的。
  • 具有不确定性,但是也远比标准C中的malloc()free()效率高!

总结:heap_2基本上适用于大多数的需要动态分配内存的工程中,而heap_4更是具有将内存碎片合并成一个大的空闲内存块(内存碎片回收)的功能。

2.2 [内存块]

  • 同heap_1一样,heap_2整个内存堆为ucHeap[],大小为configTOTAL_HEAP_SIZE。可以通过函数xPortGetFreeHeapSize()来获取剩余的内存大小。
  • 为了实现内存释放,heap_2引入了内存块的概念,每分出去一段内存就是一个内存块,剩下的一大段内存也是一个内存块,每次分的内存块大小可以不确定。
  • 为了管理内存块还引入了一个链表结构:(这是每个内存块里面都要有的链表结构体,此结构占8个字节)
    •   typedef struct A_BLOCK_LINK
        {
             
             
            struct A_BLOCK_LINK *pxNextFreeBlock;
            size_t xBlockSize;  // 此值的最高位表示当前内存块是否被使用,1为使用,0为未使用
        } BlockLink_t;
      
       _________________  ___
      | pxNextFreeBlock |  |
      |-----------------| 8 bytes
      | xBlockSize=24   |  |
      |-----------------| _|_
      |                 |
      |     16 bytes    |
      |_________________|
            <内存块>
      
      内存块图

2.3 [内存申请流程]

内存堆的初始化函数

prvHeapInit():

  1. 将内存堆ucHeap的可用起始地址pucAlignedHeap做字节对齐。
  2. 初始化结构体xStart和xEnd。此结构体是独立于内存块之外的。
  3. 把ucHeap当作一个超大的内存块
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值