RT-Thread快速入门-动态内存堆管理方法

简介: RT-Thread快速入门-动态内存堆管理方法

每种 RTOS 均有内存管理机制,RT-Thread 的内存管理分为两类:动态内存堆管理、内存池管理。

本篇文章先来介绍一下动态内存堆管理相关的内容。

第一:内存堆管理机制

RT-Thread 操作系统在内存管理上,根据上层应用及系统资源的不同,有针对性地提供了不同的内存分配管理算法。内存堆管理根据具体内存设备划分为三种情况:

  • 针对小内存块的分配管理(小内存管理算法);
  • 针对大内存块的分配管理(slab 管理算法);
  • 针对多内存堆的分配情况(memheap 管理算法)

RT-Thread 系统为了满足不同的使用需求,提供了三种内存管理算法:

  • 小内存管理算法。主要用于系统资源较少的系统。
  • slab 管理算法。主要用在系统资源比较丰富的场景。
  • memheap  管理算法。适用于系统存在多个内存堆的情况,它可以将多个内存连接在一起,形成一个大的内存堆。

备注:这几类内存堆管理算法只能启用一个,但是提供给用户的接口完全相同。

注意事项:内存堆管理为了满足多线程场景下的安全分配,考虑多线程间的互斥问题。因此,不要在中断服务程序中分配或释放动态内存块。否则,会引起当前上下文挂起,引发问题出现。

1. 小内存管理算法

这种算法比较简单。初始时,它是一块大的内存。当需要分配内存块时,将从这个大的内存块上分割出相匹配的内存块,然后把分割出来的空闲内存块还回给堆管理系统中。

每个内存块都包含一个管理用的数据头,通过这个头把使用块与空闲块用双向链表的方式链接起来,如下图所示:

小内存管理算法

数据头内容包括:

  • magic:变数(幻数),初始值为 0x1ea,用于标记这个内存块是一个内存管理用的内存数据块 。
  • used:用于标识当前内存块是否已经分配。
  • next 用于将各个内存块链接起来,指向下一个内存块节点。
  • prev 用于将各个内存块链接起来,指向当前内存节点的上一个节点。

2. slab 管理算法

RT-Thread 的 slab 分配器是在 DragonFly BSD 创始人 Matthew Dillon 实现的 slab 分配器基础上,针对嵌入式系统优化的内存分配算法。

RT-Thread 的 slab 分配器实现主要是去掉了其中的对象构造及析构过程,只保留了纯粹的缓冲型的内存池算法。slab 分配器会根据对象的大小分成多个区(zone),也可以看成每类对象有一个内存池,如下图所示:

slab 算法

一个 zone 的大小在 32K 到 128K 字节之间,分配器会在堆初始化时根据堆的大小自动调整 。

系统中 zone 的个数最大为 72,一次最大可以分配 16K 的内存空间,如果超出了 16K 那么直接从页分配器中分配  。每个 zone 上分配的内存块大小是固定的,相同大小内存块的 zone 会链接在一个链表中。

72 中对象的 zone 链表则放在一个数组中进行统一管理(zone_array[])。

(1)内存分配

分配内存时,首先从 zone_array[] 链表头数组中查找相依大小的 zone 链表。如果链表为空,则分配一个新的 zone,然后从 zone 中返回第一个空闲内存块。若非空,则返回空闲块地址。

(2)内存释放

内存释放时,分配器需要找到内存块所在的 zone 节点,然后把内存块链接到 zone 的空闲内存块链表中 。

3. memheap 管理算法

这种管理方法适用于系统中含有多个地址可不连续的内存堆。这种方法可以简化系统中存在多个内存堆时的使用:用户在系统初始化时将多个 memheap 初始化,并开启 memheap 功能,就可以把多个 memheap 粘合起来用于系统的 heap 分配。

其工作机制如下图所示。系统首先会将多个内存加入到 memheap_item 链表进行粘合。应用程序就可以在粘合后的内存堆中申请分配内存,就像在操作一个内存堆。

memheap 管理

第二:内存堆管理方式

RT-Thread 的内存堆管理操作有以下几种:初始化、申请内存块、释放内存块。

内存管理方式

需要注意的是,在使用完动态内存之后,应该将其释放掉。否则,会出现内存泄漏的问题。

1. 分配和释放内存块

RT-Thread 系统提供的动态申请内存块的函数接口如下,与我们平时接触到的 malloc() 类似。

void *rt_malloc(rt_size_t nbytes)

函数 rt_malloc() 会从系统堆空间中找到合适大小的内存块,然后把内存块首地址返回给用户。

参数 nbytes 为需要分配内存的大小,单位为字节。分配成功,则返回内存块的地址;失败,返回 RT_NULL

在申请的动态内存使用完毕后,必须及时释放,否则会造成内存泄漏。释放内存块的接口函数如下:

void rt_free (void *ptr)

此函数会把待释放的内存块还给堆管理器。

参数 ptr 为动态申请内存块的指针,即需要释放的内存块指针。如果为空指针,则直接返回。

2. 重新分配内存块

同 C 函数库类似,RT-Thread 也提供了重新分配内存块,即在已分配内存块的基础上重新分配内存块的大小。重新分配内存块时,原来的内存块数据保持不变。如果内存块缩小,则后面的数据会被截断。

重新分配内存块大小的函数接口如下:

void *rt_realloc(void *rmem, rt_size_t newsize)

参数 rmem 为指向已分配的内存块指针;newsize 为重新分配的内存大小,单位为字节。

分配成功,则返回重新分配的内存块地址;否则,返回 RT_NULL

3. 分配多个内存块

RT-Thread 也提供了从内存堆中分配连续内存的多个内存块的接口,其具体的函数原型如下:

void *rt_calloc(rt_size_t count, rt_size_t size)

参数 count 表示内存块的数量;size 表示每个内存块的大小,单位为字节。

分配成功,则返回第一个内存块地址的指针;失败,则返回 RT_NULL

该函数会把所有分配的内存块初始化为零。

第三:应用实例

老规矩,用一个示例来演示 RT-Thread 内存管理接口的使用方法。

这个例程会创建一个动态线程,这个线程动态申请内存并释放,一共申请 10 次。每次申请更大的内存,当申请不到的时候就结束,如下代码所示 :

/* 线程 1 入口 */
static void thread1_entry(void *parameter)
{ 
 int i;
 char *ptr = RT_NULL; /* 内存块指针 */
 for (i = 0; i < 10; i++)
 {
  /* 每次分配 (1 << i) 大小字节数的内存空间 */
  ptr = rt_malloc(1 << i);
  /* 如果分配成功 */
  if (ptr != RT_NULL)
  {
   rt_kprintf("get memory :%d byte\n", (1 << i));
   /* 释放内存块 */
   rt_free(ptr);
   rt_kprintf("free memory :%d byte\n", (1 << i));
   ptr = RT_NULL;
  }
  else
  {
   rt_kprintf("try to get %d byte memory failed!\n", (1 << i));
   return; 
  }
 }
}
int main()
{
 rt_thread_t thread1 = RT_NULL;
 /* 动态创建线程1 */
 thread1 = rt_thread_create("thread1", thread1_entry, RT_NULL,
     1024, THREAD_PRIORITY, THREAD_TIMESLICE);
 if(thread1 != RT_NULL)
 {
  /* 启动线程 */
  rt_thread_startup(thread1);
 }
 return 0;
}

编译运行结果如下:

代码执行结果

目录
相关文章
|
1月前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
263 1
|
1月前
|
监控 JavaScript Java
Node.js中内存泄漏的检测方法
检测内存泄漏需要综合运用多种方法,并结合实际的应用场景和代码特点进行分析。及时发现和解决内存泄漏问题,可以提高应用的稳定性和性能,避免潜在的风险和故障。同时,不断学习和掌握内存管理的知识,也是有效预防内存泄漏的重要途径。
130 52
|
2天前
|
算法 Java
堆内存分配策略解密
本文深入探讨了Java虚拟机中堆内存的分配策略,包括新生代(Eden区和Survivor区)与老年代的分配机制。新生代对象优先分配在Eden区,当空间不足时执行Minor GC并将存活对象移至Survivor区;老年代则用于存放长期存活或大对象,避免频繁内存拷贝。通过动态对象年龄判定优化晋升策略,并介绍Full GC触发条件。理解这些策略有助于提高程序性能和稳定性。
|
4月前
|
监控 Java
压力测试Jmeter的简单使用,性能监控-堆内存与垃圾回收 -jvisualvm的使用
这篇文章介绍了如何使用JMeter进行压力测试,包括测试前的配置、测试执行和结果查看。同时,还探讨了性能监控工具jconsole和jvisualvm的使用,特别是jvisualvm,它可以监控内存泄露、跟踪垃圾回收、执行时内存和CPU分析以及线程分析等,文章还提供了使用这些工具的详细步骤和说明。
压力测试Jmeter的简单使用,性能监控-堆内存与垃圾回收 -jvisualvm的使用
|
24天前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
49 5
|
26天前
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
42 6
|
2月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
85 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
2月前
|
机器学习/深度学习 算法 物联网
大模型进阶微调篇(一):以定制化3B模型为例,各种微调方法对比-选LoRA还是PPO,所需显存内存资源为多少?
本文介绍了两种大模型微调方法——LoRA(低秩适应)和PPO(近端策略优化)。LoRA通过引入低秩矩阵微调部分权重,适合资源受限环境,具有资源节省和训练速度快的优势,适用于监督学习和简单交互场景。PPO基于策略优化,适合需要用户交互反馈的场景,能够适应复杂反馈并动态调整策略,适用于强化学习和复杂用户交互。文章还对比了两者的资源消耗和适用数据规模,帮助读者根据具体需求选择最合适的微调策略。
465 5
|
2月前
|
缓存 监控 Java
在使用 Glide 加载 Gif 动画时避免内存泄漏的方法
【10月更文挑战第20天】在使用 Glide 加载 Gif 动画时,避免内存泄漏是非常重要的。通过及时取消加载请求、正确处理生命周期、使用弱引用、清理缓存和避免重复加载等方法,可以有效地避免内存泄漏问题。同时,定期进行监控和检测,确保应用的性能和稳定性。需要在实际开发中不断积累经验,根据具体情况灵活运用这些方法,以保障应用的良好运行。
|
3月前
|
缓存 Java 测试技术
谷粒商城笔记+踩坑(11)——性能压测和调优,JMeter压力测试+jvisualvm监控性能+资源动静分离+修改堆内存
使用JMeter对项目各个接口进行压力测试,并对前端进行动静分离优化,优化三级分类查询接口的性能
118 10
谷粒商城笔记+踩坑(11)——性能压测和调优,JMeter压力测试+jvisualvm监控性能+资源动静分离+修改堆内存