打印
[LKS32 软件]

为什么单片机上的程序不建议使用malloc?

[复制链接]
505|24
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
backlugin|  楼主 | 2025-2-24 01:08 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
下图,是以前自己借鉴(抄袭),再吃透,后改进的内存管理代码,测试已解决内存碎片问题。
代码没多少,却让我充分感受到,编程语言只是工具,编程思维才是灵魂。
本来是计划用在无际单片机特训营项目6的,但是感觉太复杂了,怕老铁们学着学着来骂我,所以这代码就失宠了。
新手,或者有些一直从事比较简单产品的工程师,可能无法理解,malloc的应用场景,到底在哪里?
我以无际单片机特训营项目3来举例几个使用场景,或许你就明白了。
1.malloc使用场景1:动态任务创建
学过我们项目3的老铁,不知道有没有发现一个问题。
在用我们那个"小系统"创建任务的时候,不够灵活,每次增加新的任务,要手动在头文件增加任务ID。
这样做的目的,是为了给下面这个任务结构体数组OS_Task,分配固定的内存空间。
最后才是创建任务。
如果使用动态内存分配,就可以省略前面步骤,直接创建任务,在任务创建函数里通过动态内存分配函数,给任务动态开辟一块内存,如果对RTOS有研究,应该知道我在讲什么..
2.malloc使用场景2:探测器列表
项目3是需要和不同的探测器(遥控器、门磁探测器、红外探测器、烟雾探测器等等)组网使用的。
我们做了一个菜单,在OLED屏上显示已经组网的探测器列表。
每个主机,已经组网的探测器数量都不一样,有些主机最多支持组网255个探测器。
每个探测器都有探测器ID、组网标志、序号、名称等参数。
那是不是意味着,如果主机最大支持255个探测器,如果没有动态内存分配,就要提前定义能够存储255个探测器参数的结构体数组?
事实上,我想到两种方式。
第一种是先存到外部的flash里,用到了再读出来,程序操作起来麻烦,而且效率慢,优点是省RAM。
第二种是直接分配255个探测器的静态存储空间,程序操作爽,效率高,但费RAM,还好特么用了STM32。
我这个探测器列表菜单,用的是第二种方式,因为我主机对探测器数量的上限设置是20个,哈哈。
对于这种功能需求,王炸的解决方案,就是用动态内存分配了!用时分配,用完释放!
但是,不建议直接用malloc()!!!
其实我第一次接触内存管理,是做蓝牙产品,用TI协议栈的时候。
当时有点奇怪的是,c语言标准库有malloc()动态内存分配和free()内存释放函数,osal系统为什么要自己写osal_mem_alloc()和osal_mem_free()?
直到后面自己做了一些复杂点的项目,自己也调过内存管理代码,才理解。
单片机上用malloc(),是个坑,有隐患。
我觉得内存碎片,是万恶之源。
malloc()函数本身只是动态分配内存,并没有直接解决内存碎片问题。
什么是内存碎片?
刚开始,我也不理解,什么是内存碎片,网上搜了很多相关内容,越绕越晕。
我尝试用通俗易懂的语言,长话短说,能不能理解,看基础和悟性了。
内存碎片分为两种:
1.外部碎片
想象一下,有一个大型的图书馆,图书馆的书架上摆满了各种各样的书籍,这些书籍大小可能不一样,书籍就像内存中的内存块(已被动态分配的内存),书架上的空位代表空闲内存(未被分配的内存或者被释放的内存)。
当读者借阅书籍后,书架上会留下一些空位。随着时间的推移,这些空位可能变得非常分散,就像散落在书架上的小块空间。
如果突然要存放一本很大很厚的书,到书架上时,可能很难找到足够大的连续空位来放置这本书。
那如果往后要存放的书,都是很大很厚的呢?
是不是虽然空位很多,但就是放不进去?那这块空间是不是就浪费掉了?
在内存分配时也是同理,如果频繁地用malloc()分配很多零散的内存块,每个内存块占用的字节数都不一样。
当这些内存块使用完,被free()释放以后,这块空闲内存,比如是8个字节,那下次,再有动态分配内存需求时,除非是8个字节或者以下才能使用这个内存块,如果是8个字节以上,这块内存块就相当于一直用不上,就浪费了。
所以说,即使总的空闲空间足够,但由于碎片化,也不好满足大内存块的分配请求。
这就是,在内存管理中,外部内存碎片化会导致系统无法为新的内存请求,分配足够的连续内存空间,注意连续内存空间很重要,如果不连续,处理器就要不断从整个内存池去寻找,这样读取效率就会变低,这是内存碎片的影响。
2.内部碎片
内部内部碎片就是分配了内存空间,但未被使用的部分。
为此,我做了一个实验:
上图程序里,我给p1和p2分配1个字节内存,实际却分配了8个字节的空间,在释放前这7个字节都不能再被分配,相当于7个字节空间就浪费了。
以上两种碎片的产生,会让程序产生一种很尴尬的现象,就是明明有很多空闲内存,但总是分配失败,甚至导致程序死机,而且这种死机现象,通常是没有规律的。
印象中,我以前解决碎片问题的方法,大概是,内存释放后,把该内存块后面所有已分配的内存块往前迁移。
其实内存管理,就是开辟一个很大的数组,称内存池。
然后后面所有的功能,比如动态内存分配,内存释放,都是基于这个大数组去完成,会涉及到数据结构,涉及到算法。
所以,数据结构和算法,这个时候针对性去学是最合适的。
很多人项目都没做过,就去学,没什么鸟用,学完也不知道能干嘛。
说到这里,我相信你应该没有单片机上用malloc()的勇气了吧?
小批量生产可能测不出来,大批量生产就会陆续出现死机现象了,碰到了,就偷偷躲厕所里哭吧,这种问题能找死个人!
至于很多人说的,比如单片机不用malloc(),是因为内存资源有限,个人人为不是问题本质,一般能用上动态内存分配的产品,单片机内存资源都比较大。
本质就是用malloc()容易产生内存碎片,从而会引发一系列的问题,比如数据读取效率问题、稳定性问题等等...
PC上用malloc()估计也会存在内存碎片的问题,只是电脑内存动不动就上G,没有嵌入式设备这么敏感,当然PC可能还有别的方式去解决碎片化问题,这块我没做过,不做表态。

使用特权

评论回复
沙发
两只袜子| | 2025-2-24 10:51 | 只看该作者
主要还是单片机的内存限制了

使用特权

评论回复
板凳
jcky001| | 2025-2-24 12:00 | 只看该作者
使用静态分配、内存池或许是最佳方法

使用特权

评论回复
地板
olivem55arlowe| | 2025-3-9 21:35 | 只看该作者
malloc 会在堆上动态分配内存,频繁分配和释放可能导致内存碎片。即使总内存足够,碎片化后可能无法分配连续的大块内存,最终引发程序崩溃。

使用特权

评论回复
5
rosemoore| | 2025-3-10 10:42 | 只看该作者
动态内存分配涉及查找合适的内存块,这可能会引入额外的处理时间和延迟,这在实时系统中可能是不可接受的。

使用特权

评论回复
6
1988020566| | 2025-3-10 12:15 | 只看该作者
单片机通常不需要复杂的多任务调度或动态内存管理,静态分配(如全局数组、局部数组)即可满足需求。

使用特权

评论回复
7
benjaminka| | 2025-3-10 15:19 | 只看该作者
malloc 和 free 的频繁使用会导致内存碎片化。内存碎片分为外部碎片和内部碎片

使用特权

评论回复
8
穷得掉渣大侠| | 2025-3-10 21:46 | 只看该作者
在实际开发中,如果遇到内存碎片化的问题,可以尝试使用内存池技术

使用特权

评论回复
9
febgxu| | 2025-3-11 13:46 | 只看该作者
malloc可能失败并返回NULL,这在资源受限的单片机环境中可能很难处理。单片机程序通常需要高度可靠性和确定性。

使用特权

评论回复
10
hilahope| | 2025-3-11 16:57 | 只看该作者
动态内存分配可能导致内存碎片,随着时间的推移,可用的连续内存空间可能会变得越来越少,这可能导致即使有足够的总内存,也无法分配大块连续内存的情况。

使用特权

评论回复
11
jackcat| | 2025-3-11 21:57 | 只看该作者
裸机程序中如果没有 free 回收内存,已分配的内存会永久占用,最终耗尽内存。

使用特权

评论回复
12
averyleigh| | 2025-3-12 17:16 | 只看该作者
在程序编译时确定所有变量的内存大小,这可以确保内存的分配和释放是确定的,并且没有额外的性能开销。

使用特权

评论回复
13
dspmana| | 2025-3-12 19:24 | 只看该作者
在单片机程序中,如果忘记释放分配的内存,可能会导致内存泄漏,最终耗尽可用内存。

使用特权

评论回复
14
mattlincoln| | 2025-3-12 21:35 | 只看该作者
建议使用 ​内存池​ 或 ​对象池​ 替代 malloc

使用特权

评论回复
15
hearstnorman323| | 2025-3-14 11:33 | 只看该作者
单片机的处理能力有限,malloc 和 free 的内存管理操作可能会消耗较多的处理时间,影响系统的实时性和性能。

使用特权

评论回复
16
plsbackup| | 2025-3-14 13:44 | 只看该作者
使用局部变量,它们在函数调用时自动分配和释放,这通常比动态内存分配更高效。

使用特权

评论回复
17
febgxu| | 2025-3-14 15:55 | 只看该作者
静态内存分配​ 是首选方案,既能保证实时性,又能避免内存碎片和泄漏风险。

使用特权

评论回复
18
probedog| | 2025-3-14 17:20 | 只看该作者
用静态分配、内存池等方案更有效

使用特权

评论回复
19
classroom| | 2025-3-14 19:03 | 只看该作者
碎片问题可能导致可用内存不断减少,最终无法进行有效分配,造成系统稳定性问题。因此,嵌入式开发中通常避免使用malloc,而选择更简单且可预测的内存管理方法。

使用特权

评论回复
20
beacherblack| | 2025-3-14 20:57 | 只看该作者
大多数单片机的内存资源非常有限,通常只有几十KB到几百KB的RAM可用。这些内存资源需要分配给硬件外设、堆栈、数据缓冲区等多个部分,因此留给程序动态分配的内存空间非常有限。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

14

主题

2875

帖子

1

粉丝