先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024b (备注go)
正文
最理想的状态是系统内核文件不用修改任何东西,实际上没动态内存分配根本做不到。
我们这个架构很多产品都能用,每个产品功能不一样,所以任务数量也不一样。
如果有动态内存分配,就可以给大家灵活地创建自己产品需要的任务,而不用手动改,甚至我都可以把架构的代码都封装成lib,直接提供函数接口给不同的工程师使用。
2.用于不确定数量的临时数据
比如说我们主机有配对功能,就是可以通过无线通讯去学习探测器。
然后我们设置菜单有个探测器列表,列表会显示所有已配对的探测器。
如果要把全部已经配对的探测器都显示出来,比如说我主机总共支持配对20个探测器。
那我就要实现定义出能够装下20个探测器的结构体数组。
最惨的是,还需要定义成静态的,不然下次进入这个函数,数据又丢了。
而如果我不在这个菜单的时候,实际上这块内存是浪费了的,如果有动态内存分配,那绝对是相见恨晚。
如果你还不会,赶紧学,迟早用得上!
二、内存管理如何实现?
以前我就在网上找了很多资料和例程,一边找一边骂,结果还是以失败告终。
这块是我一直想突破,一直无法突破的痛,以前也做过,但是一直无法很好地解决碎片问题。
最近运气好,经过一个高手推荐,在Github上嫖到了大神写的内存管理代码。
代码给你没用,知识嘛,学到才是自己的,哈哈。
下载下来,先深度研究,吃透以后改了个小BUG和一些细节代码,搞成Keil能编译的版本。
测试平台是我们的wifi报警项目硬件,基于STM32F103。
下图调试测试环节的痛苦过程。
是不是有种头皮发麻的感觉?那就对了! 码农的脑子从来没有舒服过。
下面是测试数据**(有点长,只截取了3/4)**:
密密麻麻的数据上一行是地址,为了方便调试和显示,我限制了最大只能分配120个字节,然后地址一个字节够用,就把高3个字节地址去掉了。
下一行是数据,2行一组,如下图所示。
Ok,下面进入本篇文章高潮部分,算法如何实现?
1.算法原理
本质就是在一个数组里面玩结构体指针,数组作为内存池。
先定义一个很大的数组,你最大支持多大内存分配,就定义多大的数组,比如说我目前最大支持120个字节,MEM_SIZE就是120。
2.数组存储方式
我们每一次分配内存给这块内存做一张”表格”,”表格”里面记录这块内存的信息。
表格用程序来表示就是结构体,因为只有结构体能表示不同类型数据的集合。
这个“表格”一共会记录内存块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都不同,自动递增
**缓存区-**0x20000078这个地址存储的数据,我程序初始化为全是3。
最终内存池里的结构就是这样的。
Ok,这个时候我调用内存释放函数mem_free(2),把内存块2的空间释放掉。
释放完以后,内存池的存储结构就成下图了。
从图中可以看出,内存块2的内存表格信息和缓存区的数据都被内存块3替代了。
所以mem_free(2)函数实现步骤大概如下:
**第一步:**先通过ID找到要释放的内存块表格信息,表格信息里有缓存区的地址。
**第二步:**通过内存块2的信息可以计算出内存块3的表格地址。
**第三步:**把内存块3的缓存区数据迁移并覆盖到内存块2的缓存区 (也就是8个字节)。
**第四步:**内存块3往内存空间高字节地址迁移8个字节(内存块2缓存区大小)后,会多8个字节数据出来,值为3,把这8个字节数据清零。
**第五步:**把内存块2的表格信息替换成内存块3的表格信息
第六步:更新内存块3表格信息里的缓存区地址改成最新的地址。
**第六步:**最后把原来存储内存块3表格信息地址的数据清掉,因为内存块3表格信息此时已在内存块2表格的位置。
这个步骤,估计大家已经看晕了。
这个不是写给你现在看的,想要理解这个代码,最好就是用串口把数据打印出来,一用看数据一边用st-link仿真分析程序。
如果没思路,再看我这个流程。
实现思路最重要,有这个思路完全可以自己写代码去实现。
至此,整个内存管理分析分享到此结束。
这个内存管理的代码还是需要进一步优化才能真正用在项目上。
比如说:
1.现在动态分配内存是返回内存块ID,实际最好返回分配出来的缓存区地址。
2.释放内存是传递内存块ID,实际最好传递缓存区地址。
3.分配和释放内存前要进入临界
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
返回分配出来的缓存区地址。**
2.释放内存是传递内存块ID,实际最好传递缓存区地址。
3.分配和释放内存前要进入临界
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-M32bkMTP-1713184216351)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!