RTOS内存管理

本文深入探讨了FreeRTOS、RT-Thread和HarmonyOS的内存管理方法,包括FreeRTOS的几种动态内存管理算法(heap_1至heap_5),RT-Thread的静态和动态内存管理(小内存管理、slab管理和memheap),以及HarmonyOS的内存管理策略。讨论了各自的优缺点和适用场景,强调了线程安全和内存碎片管理的重要性。
摘要由CSDN通过智能技术生成

0. 内存管理概述

内存管理涉及基本的代码程序概念见代码重定位 - ARM裸机_arm代码重定位-CSDN博客和基本的文件与运行时代码地址布局程序内存分布和堆、栈概念_并无bin文件里包含栈-CSDN博客

裸机程序只有一个独立的主进程工作,没有复杂的内存管理,其地址布局按照程序的编译以后的结果,根据需要指定栈地址为固定的一个数组既可以正常运行。RTOS系统一般会出现多进程工作的情况,为了较好的使用好内存信息,会有malloc结合多个进程完成内存的高效使用,但这种系统一般没有虚拟内存。Linux系统内存管理不仅有复杂的算法同时也包含了虚拟内存的内容。Linux内存管理详细见专题。

RTOS内存管理常用动态内存管理和静态内存管理两种方式,两种方式各有优缺点:

  • 动态方法:自动地从管理的内存堆中申请创建对象所需大小的内存,并且在对象删除后可自动地将这块内存释放回内存堆。优点是按需分配,设备中灵活使用;缺点是内存池中可能出现碎片。
  • 静态方法创建:由用户手动地提供各种内存空间,并且使用静态方式占用的内存空间一般固定下来了,即使任务、队列等被删除后,这些被占用的内存空间一般没有其他用途 ,我们一般是不会再去使用了。优点是分配和释放效率高,内存池中无碎片;缺点是只能申请预设大小的内存块,不能按需申请存在内存浪费的情况。

对于动态内存管理方法,标准 C 库也提供了函数 malloc() 和函数 free() 来实现动态地申请和释放内存 。为啥不用标准的 C 库自带的内存管理算法?因为标准 C 库的动态内存管理方法有如下几个缺点:

  • 占用大量的代码空间 不适合用在资源紧缺的嵌入式系统中。而 FreeRTOS 号称是轻量级的,它的一个内存管理算法就几百行代码,所以相较之下 FreeRTOS 的代码量更小。
  • 没有线程安全的相关机制。很明显 C 库又不是专门为你 os 而设计的,像 os,它任务与任务之间是会抢占的,那这个呢就涉及到线程安全了,而 FreeRTOS 提供的内存管理算法,那很显然它就是为它自己而设计的,它里面就考虑到这个线程安全了。在我们要申请的时候,它就会挂起任务调度器,那任务切换不了,就打断不了了。
  • 运行有不确定性,每次调用这些函数时花费的时间可能都不相同。
  • 内存碎片化,多次申请释放之后,可用的内存空间就越来越小。

1. FreeRTOS内存管理

FreeRTOS 就是因为 C 库它这个内存管理算法有这么多缺点,因此,它提供了多种动态内存管理的算法,可针对(适配)不同的嵌入式系统!FreeRTOS提供了5种动态内存管理算法,分别为: heap_1、heap_2、heap_3、heap_4、heap_5。其实都是一个 .c 文件,heap_1.c、heap_2.c、… … 这样下去,提供了 5 个 .c 文件。那这 .c 文件就是它们内存管理算法的实现。

算法实现优点缺点使用场景
heap_1

通过一个ucHeap[],大小为configTOTAL_HEAP_SIZE的数组定义一个堆内存,当然也可以通过指针的方式指定这块连续内存块大小表示一个堆。

1. 分配内存直接通过当前使用指针位置开始获取指定大小的内存,返回给使用程序。

分配简单,时间确定只允许申请内存,不允许释放内存,内存利用率不高

内存可以提前固定分配,实际嵌入式中这种很常见

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

heap_2通过链表串联空闲内存块,在基础上适配按照大小排序的最佳匹配算法实现内存管理允许申请和释放内存不能合并相邻的空闲内存块会产生碎片、时间不定

内存申请释放顺序不变且大小不变场景

①可以使用在那些可能会重复的删除任务、队列、信号量等的应用中,要注意有内存碎片产生!
②如果分配和释放的内存大小是随机的,那么就要慎重使用了。
③如果应用中的任务、队列、信号量和互斥信号量具有不可预料性(如所需的内存大小不能确定,每次所需的内存都不相同,或者说大多数情况下所需的内存都是不同的)的话可能会导致内存碎片。
④具有不可确定性,但是也远比标准C中的mallo()和free()效率高!

heap_3标准C中的函数malloc()和free()的简单封装,FreeRTOS对这两个函数做了线程保护。直接调用C库函数malloc()和 free() ,在 C 库基础上增加了线程安全,简单速度慢、时间不定

空间较大,时间要求不敏感

①需要编译器提供一个内存堆,编译器库要提供malloc()和free()函数。比如STM32的话可以通过修改启动文件中的Heap_Size来修改内存堆的大小。
②具有不确定性。
③可能会增加代码量。
注意:在heap_3.c中configTOTAL_HEAP_SIZE是没用的!

heap_4提供了最优匹配算法和内存合并算法。与heap_2.c相比较,为了适配较好的合并,排序算法基于地址指针完成。相邻空闲内存可合并,减少内存碎片的产生时间不定

灵活内存需要的方案

①可是用在那些需要重复创建和删除任务、队列、信号量和互斥信号量等的应用中。
②不会像heap_2.c那样产生严重的内存碎片,即使分配的内存大小是随机的。
③具有不确定性,但是远比C标准库中的malloc()和free()效率高。heap_4.c非常适合于那些需要直接调用函数pvPortMalloc()和vPortFree()来申请和释放内存的应用。

heap_5heap_5.c使用了和heap_4.c相同的合并算法,内存管理实现起来基本相同,但是heap_5.c允许内存堆跨越多个不连续的内存段。比如STM32的内部RAM可以作为内存堆,但是STM32内部RAM比较小,遇到那些需要大容量RAM的应用就不行了,如音视频处理。能够管理多个非连续内存区域的 heap_4时间不定图形显示芯片类,内部RAM空间不够外扩内存的时候使用

总结:​FreeRTOS官方提供的5种内存分配方法已经介绍完,heap_1.c最简单,但是只能申请内存,不能释放。heap_2.c提供了内存释放函数,用户代码也可以直接调用函数pvPortMalloc()和vPortFree()来申请和释放内存,但是heap_2.c会导致内存碎片的产生!heap_3.c是对标准C库中的函数malloc()和free()的简单封装,并且提供了线程保护。heap_4.c相对与heap_2.c提供了内存合并功能,可以降低内存碎片的产生,我们移植FreeRTOS的时候就选择了heap_4.c。heap_5.c基本上和heap_4.c一样,只是heap_5.c支持内存堆使用不连续的内存块。

参考:

  1. FreeRTOS的内存管理方法(超详细)_rtos内存管理-CSDN博客
  2. FreeRTOS 内存管理_freertos内存管理-CSDN博客
  3. FreeRTOS学习-内存管理_freertos内存管理-CSDN博客

2. RT-Thread内存管理

1.1 静态内存管理

内存池(Memory Pool)是一种用于分配大量大小相同的小内存对象技术,应用程序可以直接在现有的池子中获取资源,分配和释放速度快。内存池创建时先向系统申请一块大内存,然后分成大小相等的多个小内存块,小内存块通过链表连接起来。

2.1 动态内存管理

其中,动态内存堆管理又根据具体设备的RAM大小划,分为三种情况:

  • 小内存管理算法主要针对系统资源比较少,一般用于小于 2MB 内存空间的系统(小内存 管理算法);
  • slab 内存管理算法则主要是在系统资源比较丰富时,提供了一种近似多内存池管理算法的快速算法(slab 管理算法);
  • RT-Thread 还有一种针对多内存堆的管理算法,即 memheap 管理算法。memheap 方法适用于系统存在多个内存堆的情况,它可以将多个内存 “粘贴” 在一起,形成一个大的内存堆(memheap 管理算法);

注意:因为内存堆管理器要满足多线程情况下的安全分配,会考虑多线程间的互斥问题,所以不要在中断服务例程中分配或释放动态内存块,因为它可能会引起当前上下文被挂起等待;

2.1 小内存管理法

  • 当需要分配内存块时,将从这个大的内存块上分割出相匹配的内存块,然后把分割出来的空闲内存块还回给堆管理系统中。每个内存块都包含一个管理用的数据头,通过这个头把使用块与空闲块用双向链表的方式链接起来,如图所示:

  • 每个内存块都包含一个数据头:magic 和 used
  • magic:用来标记这个内存块是一个内存管理的内存数据块,也是一个内存保护字,如果这个区域被改写,那么这个内存块就被非法改写了;
  • used :用来表示当前内存块是否被分配;
struct heap_mem
{
    /* magic and used flag */
    rt_uint16_t magic;
    rt_uint16_t used;

    rt_size_t next, prev;

#ifdef RT_USING_MEMTRACE
    rt_uint8_t thread[4];   /* thread name */
#endif
};
  • 内存分配过程:

  • 设定:空闲链表指针Ifree初始指向32字节的内存块,当用户线程需要分配一个64字节的内存块时,Ifree指针指向的内存块不能满足要求,内存管理器就会继续寻找下一个内存块,当找到时(128字节),就会进行内存分配,如果内存块比较大,分配器就会把内存块进行拆分,余下的内存(52字节)继续留在Ifree链表中,如下图所示;

  • 注意:在内次分配内存块前,都会留出12字节用于magic、used信息及节点使用,返回给应用的地址实际上是这块内存块 12 字节以后的地址,前面的 12 字节数据头是用户永远不应该碰的部分(注:12 字节数据头长度会与系统对齐差异而有所不同)。
  • 释放:释放内存时,分配器会查看前后相邻的内存是否是空闲,如果空闲,就回合并成一个大的空闲内存块; 

4.2 slab 管理算法

  • RT-Thread 的 slab 分配器的实现是建立在 slab分配器上的,针对嵌入式仙童优化的内存分配算法。(slab是linux系统中的一种内存分配机制)
  • RT-Thread 的 slab 分配器实现主要是去掉了其中的对象构造及析构过程,只保留了纯粹的缓冲型的内存池算法。slab 分配器会根据对象的大小分成多个区(zone),也可以看成每类对象有一个内存池,如下图所示:

  • 一个 zone 的大小在 32K 到 128K 字节之间,分配器会在堆初始化时根据堆的大小自动调整。系统中的 zone 最多包括 72 种对象,一次最大能够分配 16K 的内存空间,如果超出了 16K 那么直接从页分配器中分配。每个 zone 上分配的内存块大小是固定的,能够分配相同大小内存块的 zone 会链接在一个链表中,而 72 种对象的 zone 链表则放在一个数组(zone_array[])中统一管理。
  •  内存分配:
  • 假设分配一个 32 字节的内存,slab 内存分配器会先按照 32 字节的值,从 zone array 链表表头数组中找到相应的 zone 链表。如果这个链表是空的,则向页分配器分配一个新的 zone,然后从 zone 中返回第一个空闲内存块。如果链表非空,则这个 zone 链表中的第一个 zone 节点必然有空闲块存在(否则它就不应该放在这个链表中),那么就取相应的空闲块。如果分配完成后,zone 中所有空闲内存块都使用完毕,那么分配器需要把这个 zone 节点从链表中删除。
  • 内存释放:分配器需要找到内存块所在的 zone 节点,然后把内存块链接到 zone 的空闲内存块链表中。如果此时 zone 的空闲链表指示出 zone 的所有内存块都已经释放,即 zone 是完全空闲的,那么当 zone 链表中全空闲 zone 达到一定数目后,系统就会把这个全空闲的 zone 释放到页面分配器中去。

4.3 memheap 管理算法

当使用的芯片有两个块SRAM(如STM32F427)或者外挂SRAM时可以使用此种内存管理方法。如果有多个不连续的 memheap 可以多次调用该函数将其初始化并加入 memheap_item 链表。

  • memheap 管理算法适用于系统含有多个地址可不连续的内存堆。使用 memheap 内存管理可以简化系统存在多个内存堆时的使用:当系统中存在多个内存堆的时候,用户只需要在系统初始化时将多个所需的 memheap 初始化,并开启 memheap 功能就可以很方便地把多个 memheap(地址可不连续)粘合起来用于系统的 heap 分配;
  • 在开启 memheap 之后原来的 heap 功能将被关闭,两者只可以通过打开或关闭 RT_USING_MEMHEAP_AS_HEAP 来选择其一;
  • memheap 工作机制如下图所示,首先将多块内存加入 memheap_item 链表进行粘合。当分配内存块时,会先从默认内存堆去分配内存,当分配不到时会查找 memheap_item 链表,尝试从其他的内存堆上分配内存块。应用程序不用关心当前分配的内存块位于哪个内存堆上,就像是在操作一个内存堆。

参考:

  1. rt-thread-------内存管理(内存堆)_rtthread内存管理-CSDN博客
  2. https://www.cnblogs.com/icefree/p/10822971.html#_label1_1

3. HarmonyOS内存管理

4. nuttx 内存管理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值