浅谈malloc函数在单片机上的应用

 2024-02-08 03:02:44  阅读 0

	聊聊 malloc函数 在单片机程序设计中怎么使用

目录

前言

最近更新RT-专栏,说到内存管理的时候,我就思考如何解释这个内存管理。 其实用STM32做一般产品时,功能基本用不到。 即使使用了操作系统,在业务逻辑上如果不复杂的话,还是没有什么用处。

然而,每个嵌入式RTOS都会有自己的内存管理方法。 本文将谈谈我对单片机编程中函数的一些看法。

本文并不是讲解单片机中的函数如何使用,而是根据博主自己的理解来分析是否需要使用、何时何地使用。

本文讨论微控制器领域,以-M系列内核为例。

1. 功能介绍

中文全称是动态内存分配。

函数原型void *(int size),专业解释仍然适用百度百科:

所有嵌入式工程师都应该了解功能。 即使他们从未使用过它们,他们也听说过它们。 通过上面的简单解释,他们也能知道它们是用来做什么的。

注意上面红框中,开辟了一个连续的空间,并返回了一个地址。 当内存不使用时,需要使用free()函数释放内存。

2. 对于微控制器

在我们的单片机编程中,我们大部分使用的是C语言。 当然,我们可以使用函数,但是很多人并没有真正理解它。

要了解单片机系统中的功能,首先要了解动态分配的内存在哪里!

2.1 函数请求的内存在哪里?

可能大多数人都知道在堆里吧! 是的,就在堆里。

那么下一个问题是,它堆叠在微控制器的哪里? 具体地址是什么?

要理解这个问题,就得了解内核的数据存储方式。 我们以常用的STM32为例。 我在下面的博文中详细介绍了STM32的内存管理(如果你不明白,建议你先阅读这篇博文):

STM32内存管理相关(内存架构、内存管理、map文件分析)

首先我们简单了解一下单片机的堆栈。 文章中有如下解释:

如果我们通过上面推荐的博文了解了内存分配,那么我们可以得出以下结论,知道申请空间的具体地址:

2.2 使用与不使用的区别

现在我们知道了单片机中应用程序空间的地址,我们来看看应用程序之间的差异。

一般在我们的设计中,可能会在函数中初始化一些临时变量。 如果是数组,那么也会申请一块内存空间。 我们用一张图来看看使用临时变量和使用临时变量的区别:

讲解到这里,相信大家对于在单片机上的使用有了更深入的了解。 它存储的空间和我们常规的局部变量的空间不同,我们也知道它在哪里。

至于单片机是否应该使用函数? 别着急,下面我们还是要分析一下。

3、可能遇到的问题

当工作机制还在百度官方百科介绍时,有如下解释:

注意图中红色部分。 简单的解释就是,使用过多的函数后,会产生大量的内存碎片,造成内存的浪费。

3.1 内存碎片

什么是内存碎片?

这种专业术语就要靠万能的百度了(虽然百度百科上的解释是针对广泛的,但其实对于单片机来说也是一样的):

系统中所有不可用的空闲内存都是内存碎片。

那么内存碎片是如何产生的呢?

上图中,其实已经解释了碎片是如何产生的。 内部碎片是由于处理器的架构造成的,需要字节对齐。 例如,在微控制器中,我们经常有4字节对齐和8字节对齐。 别提这个了我不知道,我只是打开了一个启动文件说明(GCC环境下的链接文件):

根据这个启动文件我们也应该知道堆栈内存空间需要8字节对齐,所以我们在STM32上分配的内存空间是8字节对齐的。 即使你不使用8字节,系统也会给你添加。

说到这里,正好解释一下我们内存碎片的第一种情况,内部碎片的产生:

那么我们还可以用图形来表示外部片段的生成:

随着内存的不断分配和释放,整个内存区域会产生越来越多的碎片,因为在使用过程中,有的内存被申请,有的内存被释放,导致内存空间中出现一些小内存块。 它们的地址不连续,不能作为一整块大内存进行分配。 系统中仍然有足够的空闲内存,但由于它们的地址不连续,无法形成连续的完整内存块,这会妨碍程序申请大内存。

在我们使用的单片机上,碎片问题尤为明显,平时无法访问。 那是因为一般的学习测试不会遇到复杂的项目。

3.2 内存管理

会造成碎片,那么有没有办法解决这个问题呢?

当然有,那就是内存管理。

内存管理就是为了解决上面提到的内存碎片问题,如何高效、快速地分配,并在适当的时候释放和回收内存资源。

对于单片机来说,如果有能力,可以设计自己的内存管理方法。

如果您使用RT-等嵌入式操作系统,它们的内核有自己的内存管理。 本文不会讨论它们具体是如何管理内存的,但是有必要了解一下操作系统的思维。

举个例子:

中有一个宏定义E:

操作系统首先向系统申请一大块内存。 该内存是由操作系统自己的内存管理方法来管理的。 操作系统的内存管理方式有5种:

我们在设计时可以选择使用哪一种,例如:

所申请的内存由操作系统自动管理。 对于操作系统创建的任务,任务堆栈使用该内存。 同时,如果用函数申请动态内存,也会从这块内存中分配,因为它有一套完整的内存。 管理方法,所以相比我们直接使用,它可以很好的处理系统内存碎片的问题。

既然我们说了这些,那么附加一个问题是,E定义的存储器的哪一部分位于单片机的存储器中?

这里你可以阅读我的另一篇博文:嵌入式RTOS的任务栈和系统栈

所请求的内存属于.bss段,其位置如下图所示:

对于微控制器所使用的嵌入式操作系统来说,它们有自己的内存管理方法,也会提供相应的动态内存申请结构。 这时我们就利用操作系统提供的接口函数来避免内存碎片的产生。

注意! ! 微控制器采用带内存管理的操作系统,系统会提供相应的API,如函数、RT-函数等。

如果使用C库,仍然会从系统堆中申请内存! !

对于高端微控制器,有MMU(内存管理单元)模块,例如-A系列。 有了MMU,就可以运行Linux了,所以内存管理也是必须的。

4. 结论(用还是不用?)

本文详细分析了在微控制器上使用函数的效果。 我们知道函数申请的内存空间在哪里,也知道内存碎片是如何产生的。

回到我们最初的问题,在单片机领域,我们到底该用函数还是不用函数呢?

读完这篇文章,我可能不需要直接回答这个问题:

从项目复杂程度来看:

如果你是跑裸机做小项目,如果没有自己的内存管理方法,非必要的话不建议使用。 同时,为了节省内存,可以将堆设置得很小(留一些给可能调用的C库函数)。

如果你运行一个操作系统,操作系统有完整的内存管理,你就可以愉快地使用操作系统的接口函数了。

但如果做一些小项目,就不需要用它了。

如果您运行裸机来执行大型项目怎么办? ? ? ? 我个人不推荐...

就所使用的芯片 RAM 大小而言:

如果你选择的芯片内存比较小,10K以上甚至几K,最好用抖音(),因为可以使用小内存的项目都不会太复杂,比如物联网传感器单一产品项目。

如果所选芯片内存大于MB,仍然可以尝试使用动态内存分配,但前提是必须有内存管理。

但最后不得不说,随着当今单片机的发展,内存越来越大。 虽然对于小型单片机项目不建议使用函数,但是我们安装完操作系统之后,一定要学会使用动态内存分配,因为以后做的项目越来越复杂,也越来越多。线程,并且我们正在定义越来越多的局部变量。 即使我们可以继续增加系统堆栈的大小,但这毕竟不是一个合理的解决方案。

我们要学会合理利用动态内存的应用,才能将来走向更高的地方~

如果项目很简单,只是想用一下,就没有必要深究。 比如我为一个函数申请动态内存,没有内存管理,怎么办? 在这种情况下,使用与否并不重要。 只要确保你快乐就可以了,没有必要担心!

谢谢!

如本站内容信息有侵犯到您的权益请联系我们删除,谢谢!!


Copyright © 2020 All Rights Reserved 京ICP5741267-1号 统计代码