FreeRTOS学习(八)内存管理

1.内存分区

ROM和RAM的扩展阅读

  在C语言中定义了4个区:代码区、全局变量和静态变量区、动态变量区(即栈区)、动态存储区(即堆区)。

  1. 栈区(stack): 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。在STM32汇编代码中设置如下:
Stack_Size      EQU     0x00000400
                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp
  1. 堆区(heap): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。这里主要用于mallocfree函数申请的区域,如果没有用于mallocfree函数,可以设置为0。在STM32汇编代码中设置如下:

也就是说,如果代码中没有使用mallocfree函数,则没有堆区。

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit
  1. 全局变量和静态变量区(对应数据区):全局变量和静态变量(static修饰的变量)存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 程序结束后由系统释放。
  2. 常量区:常量字符串就是放在这里的。
  3. 程序代码区:存放函数体的二进制代码

  通过编译STM32的工程文件,可以看到如下编译的结果如下:
在这里插入图片描述

  • RORead-Only的缩写,包括RO-data(只读数据)和RO-code(代码)。

  • RWRead-Write的缩写,主要是RW-dataRw-data由程序初始化初始值。

  • ZIZero-initialized的缩写,主要是ZI-data,由编程器初始化为0。注:在keil中,栈区被默认是ZI段的子集。

  STM32的内存分配从0X20000000开始,分为两种情况:

使用 malloc未使用 malloc
静态存储区+堆区+栈区静态存储区+栈区

在这里插入图片描述
在这里插入图片描述

一般对于我们开发板例程,实际上,没有所谓堆区的概念,而仅仅是:静态存储区+栈区。
无论哪种情况,所有的全局变量,包括静态变量之类的,全部存储在静态存储区。
紧跟静态存储区之后的,是堆区(如没用到malloc,则没有该区),之后是栈区。

2.内存管理概述

2.1 必要性

  在创建任务时,需要申请内存来存放任务控制块和任务堆栈,同样地,创建队列时也需要内存来存放队列结构体和队列存储区。在开发应用中,肯定要创建和删除一些任务或队列等,那么它们所需内存的申请和释放必须有一定的方法来管理。
  在介绍内存管理方法之前,先来看看FreeRTOS提供的可用内存,在FreeRTOS的内存管理方法 heap_x.c中定义了一个uint8_t 全局静态数组 ,任务/队列所需要的内存均从这个数组中申请获取,即从静态存储区申请内存

#if ( configAPPLICATION_ALLOCATED_HEAP == 1 )						// FreeRTOSConfig.h中未定义该宏,为 0
    extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];					// 用户自定义
#else
    PRIVILEGED_DATA static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];	//编译器决定,默认采用这种方法
#endif 

  configTOTAL_HEAP_SIZE 宏在FreeRTOSConfig.h头文件中定义

#define configTOTAL_HEAP_SIZE             ((size_t)(15 * 1024))
  • 任务控制块在全局变量内申请,因此内核的不同函数均可对任务进行管理
  • 申请内存时使用 pvPortMalloc() 来申请,释放内存时,使用vPortFree()来释放内存
2.2 内存碎片

  内存碎片是内存管理不可忽略的一个问题,内存碎片产生的原因在于不同内存大小的多次申请和释放。如下图,释放的小内存10B,在申请大内存20B时用不上,或者申请 8B,剩余2B,其他申请无法使用,多次申请和释放后产生很多内存碎片。
在这里插入图片描述

2.3 字节对齐

  在FreeRTOS中分配内存时,都必须进行字节对齐,主要目的是提升效率。

比如说,一个 uint32_t 的变量,如果没有字节对齐,32MCU 可能需要读取两次才能获取这个数据。
在这里插入图片描述

  在内存分配时会存在两个地方进行字节对齐,分别是内存堆字节对齐申请内存字节对齐
(1)内存堆字节对齐
  以 heap_4.c 为例,在第一次调用内存申请函数时会初始化内存堆prvHeapInit()

uxAddress = ( size_t ) ucHeap;

if( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 )			// 进行堆内存字节对齐
{
    uxAddress += ( portBYTE_ALIGNMENT - 1 );
    uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
    xTotalHeapSize -= uxAddress - ( size_t ) ucHeap;		// 字节对齐后,少了几个字节,重新计算可用大小
}

pucAlignedHeap = ( uint8_t * ) uxAddress;					// 字节对齐后的可用起始地址

在这里插入图片描述
(2)申请内存字节对齐
  申请内存的大小最好是 8 的倍数,不然也会自动触发字节对齐,比如创建任务申请堆栈为 125 个字节,字节对齐后申请的字节为 128个。

3.内存管理方法

  FreeRTOS提供了五种内存分配方法,经常使用的是 heap_4heap_4 是在 heap_2 上扩展得来的,因此着重介绍下这两种内存分配方法。

heap_1:只能申请,不能释放
heap_3:将标准的malloc()和free()进行了简单的封装

3.1 heap_2 方法

  heap_2方法中引入了内存块链表结构,内存块前面有一个BlockLink_t类型的变量来描述内存块,占用8个字节,即如果申请16个字节,实际申请了24 个字节。这些内存块由一个链表管理,这个链表称为空闲内存块列表
在这里插入图片描述
  内存申请过程如下,值得注意的是内存块是按照内存从小到大的顺序排序在空闲内存块链表中。
在这里插入图片描述
  内存释放过程比较简单,主要是将需要释放的内存所在的内存块添加到空闲内存块链表中

3.2 heap_4 方法

  heap_2 方法在内存释放时只是将内存块插入到链表中,并未实现相邻内存块的合并,而这一点在 heap_4 方式中实现了,即通过合并算法将相邻的内存块合并成一个大内存块
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

la_fe_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值