一文搞懂栈(stack)、堆(heap)、单片机裸机内存管理malloc_keil stack heap

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注go)
img

正文

内存碎片就是分配了内存空间,但是未被使用的部分。

比如说你用malloc(1)分配了1个字节,但实际给你分配了8个字节的空间,剩余7个就是内存碎片。

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

我们通过程序来演示一下:

图片

我们在给p1和p2分配的时候,明明只分配1个字节,实际却分配了8个字节的空间,在释放前这7个字节都不能再被分配,相当于7个字节空间就浪费了。

这是其中一个产生碎片的方式,除此以外,还有别的方式会产生。

比如说你第一次连续申请了2个空间,第一块是1个字节,第二块也是1个字节。

理论上分配的空间地址都是连续的,但是中间产生7个字节内存碎片,分配两块的话就是14个字节。

当把第一块1个字节释放以后,第二块1个字节的空间还没释放。

这样相当于第一块的空间只能用来分配1个左右字节的空间了(有可能还可以分配2-6个字节的),具体要看Malloc()函数分配算法。

但可以肯定的是,不能分配像10个字节这么大的空间,那这块空间的应用范围就会缩小了很多

如果一个程序分配很多1个字节这种小空间,那后面整个内存块会有非常多这种碎片叠加。

最后会导致,明明有很多空闲内存,但是总是分配失败,甚至导致程序崩溃。

所以,这就是要自己写内存管理的原因,就是要解决内存碎片这种痛点

内存管理由很多不同的子功能组成,比如说动态内存分配算法、内存释放等等。

但是内存管理做起来是比较复杂的,涉及到数据结构和一些小算法。

有些高端的单片机为了帮工程师解决繁琐的内存管理代码,就内置了MMU(内存管理单元)模块

不过大多数单片机都没有,我自己也没用过,没有的就要自己写代码去实现内存管理。

内存管理可以说是实时操作系统和自己写程序架构的刚需,操作系统一般有自带不用自己写

一、动态分配内存实际应用有哪些?

拿我们wifi报警主机这个项目为例。

1.用于任务灵活创建

我们的主机自己写了一个小系统,有涉及到任务创建和调度。

我们在创建任务的时候就非常不灵活,需要手工去调整内核头文件的任务数量。

img

最理想的状态是系统内核文件不用修改任何东西,实际上没动态内存分配根本做不到。

我们这个架构很多产品都能用,每个产品功能不一样,所以任务数量也不一样。

如果有动态内存分配,就可以给大家灵活地创建自己产品需要的任务,而不用手动改,甚至我都可以把架构的代码都封装成lib,直接提供函数接口给不同的工程师使用。

2.用于不确定数量的临时数据

比如说我们主机有配对功能,就是可以通过无线通讯去学习探测器。

然后我们设置菜单有个探测器列表,列表会显示所有已配对的探测器。

图片

如果要把全部已经配对的探测器都显示出来,比如说我主机总共支持配对20个探测器。

那我就要实现定义出能够装下20个探测器的结构体数组。

图片

最惨的是,还需要定义成静态的,不然下次进入这个函数,数据又丢了。

而如果我不在这个菜单的时候,实际上这块内存是浪费了的,如果有动态内存分配,那绝对是相见恨晚。

如果你还不会,赶紧学,迟早用得上!

二、内存管理如何实现?

以前我就在网上找了很多资料和例程,一边找一边骂,结果还是以失败告终。

这块是我一直想突破,一直无法突破的痛,以前也做过,但是一直无法很好地解决碎片问题。

最近运气好,经过一个高手推荐,在Github上嫖到了大神写的内存管理代码。

代码给你没用,知识嘛,学到才是自己的,哈哈。

下载下来,先深度研究,吃透以后改了个小BUG和一些细节代码,搞成Keil能编译的版本。

测试平台是我们的wifi报警项目硬件,基于STM32F103。

下图调试测试环节的痛苦过程。

图片

图片

是不是有种头皮发麻的感觉?那就对了! 码农的脑子从来没有舒服过。

下面是测试数据**(有点长,只截取了3/4)**:

图片

密密麻麻的数据上一行是地址,为了方便调试和显示,我限制了最大只能分配120个字节,然后地址一个字节够用,就把高3个字节地址去掉了

下一行是数据,2行一组,如下图所示。

img

Ok,下面进入本篇文章高潮部分,算法如何实现?

1.算法原理

本质就是在一个数组里面玩结构体指针,数组作为内存池。

img

先定义一个很大的数组,你最大支持多大内存分配,就定义多大的数组,比如说我目前最大支持120个字节,MEM_SIZE就是120。

2.数组存储方式

我们每一次分配内存给这块内存做一张”表格”,”表格”里面记录这块内存的信息。

表格用程序来表示就是结构体,因为只有结构体能表示不同类型数据的集合。

img

这个“表格”一共会记录内存块3个信息:内存块数据的存储地址、内存块大小、内存块ID

这3个信息是为后面写动态内存分配和释放内存函数作铺垫的,目的是更好地寻找到指定的内存块。

相当于每动态分配一块内存,都会在内存池(数组)里面分配两块内存空间。

一块内存是用来存储这块内存唯一的表格(结构体),根据结构体成员计算的话就是固定的8个字节。

另一块内存就是实际你需要分配的内存空间大小,最终你的数据就是存在这块内存里。

比如说,当我调用动态内存分配函数mem_malloc(10),分配了10个字节的内存空间,并且全部写入值1

完成以后,内存池的存储结构如下:

图片

然后,我再调用动态内存分配函数mem_malloc(8),又分配了8个字节的内存空间,并且全部值写入2

完成以后,内存池的存储结构变化如下:

图片

经过这两次分配内存以后,不知道你发现了没有。

内存是连续分配的,没有碎片。

内存低地址空间保存内存块信息(“表格”),高地址空间分配用户的缓存,有没有感觉跟前面堆栈的使用一样?都是往内存空间中间分配。

数据的低位存储在内存的低地址中数据的低位存储在内存的低地址中(即小端模式)

3. mem_malloc()动态内存分配函数

mem_malloc()函数就是以上面说的分配原理,然后用代码去实现,源代码如下:

图片

这个函数相对简单,注释也详细,大家可以自己研究下。

4.mem_free()内存释放函数

真正的难点也就是在这个函数,主要是去碎片的算法。

先贴上这个函数的代码:

图片

实现原理:

比如我现在动态分配了3块内存空间,每个内存块对应信息如下。

内存块1:

**内存地址-**94 00 00 20(十六进制),转换成高位在前就是0x20000094

**内存大小-**0a 00,换算成10进制就是10个数据,代表缓存区大小是10个字节

**内存ID-**01 00,每个内存块ID都不同,自动递增

**缓存区-**0x20000094这个地址存储的数据,我程序初始化为全是1。

内存块2:

内存地址- 8c 00 00 20 (十六进制),转换成高位在前就是0x2000008c

内存大小- 08 00,换算成10进制就是8个数据,代表缓存区大小是8个字节

**内存ID-**02 00,每个内存块ID都不同,自动递增

**缓存区-**0x2000008c这个地址存储的数据,我程序初始化为全是2。

内存块3:

内存地址- 78 00 00 20 (十六进制),转换成高位在前就是0x20000078

内存大小- 14 00,换算成10进制就是20个数据,代表缓存区大小是20个字节

**内存ID-**03 00,每个内存块ID都不同,自动递增

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
** 14 00,换算成10进制就是20个数据,代表缓存区大小是20个字节

**内存ID-**03 00,每个内存块ID都不同,自动递增

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-VcdGfiyq-1713448515203)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 20
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值