18 | 划分土地(下):如何实现内存页的分配与释放?
LMOS
该思维导图由 AI 生成,仅供参考
你好,我是 LMOS。
通过前面两节课的学习,我们已经组织好了内存页,也初始化了内存页和内存区。我们前面做了这么多准备工作,就是为了实现分配和释放内存页面,达到内存管理的目的。
那有了前面的基础,我想你自己也能大概实现这个分配和释放的代码。但是,根据前面我们设计的数据结构和对其初始化的工作,估计你也可以隐约感觉到,我们的内存管理的算法还是有一点点难度的。
内存页的分配
如果让你实现一次只分配一个页面,我相信这个问题很好解决,因为你只需要写一段循环代码,在其中遍历出一个空闲的 msadsc_t 结构,就可以返回了,这个算法就可以结束了。
但现实却不容许我们这么简单地处理问题,我们内存管理器要为内核、驱动,还有应用提供服务,它们对请求内存页面的多少、内存页面是不是连续,内存页面所处的物理地址都有要求。
这样一来,问题就复杂了。不过你也不必担心,我们可以从内存分配的接口函数下手。
下面我们根据上述要求来设计实现内存分配接口函数。我们还是先来建立一个新的 C 语言代码文件,在 cosmos/hal/x86 目录中建立一个 memdivmer.c 文件,在其中写一个内存分配接口函数,代码如下所示。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
LMOS系列课程的第三节深入讨论了内存页的分配与释放,重点关注内存分配接口函数的设计和实现,以及内存分配的核心函数。通过深入的技术讲解和实践操作,帮助读者理解了分配算法的核心逻辑,使得复杂的算法变得更加易于理解。文章还介绍了释放内存页面的逻辑,包括释放内存页面的核心函数和框架函数的设计与实现,以及释放算法的核心逻辑。整体而言,本文通过深入的技术讲解和实践操作,为读者提供了全面的内存分配与释放知识,对于深入理解内存管理具有重要的参考价值。 本文重点回顾了内存分配接口、框架、核心处理函数的实现,以及释放页面的接口、框架、核心处理函数的释放算法。同时,提出了内存管理器只能分配页面的缺陷,并引发读者思考如何改进。下一节课将继续讨论更好的方案。文章还提出了思考题,引导读者思考如何尽可能保证内存页面连续。通过文字、代码和图示结合,帮助读者理解了分配算法的核心逻辑,使得复杂的算法变得更加易于理解。LMOS系列课程以深入的技术讲解和实践操作为特点,适合对内存管理感兴趣的技术人员学习。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《操作系统实战 45 讲》,新⼈⾸单¥68
《操作系统实战 45 讲》,新⼈⾸单¥68
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(13)
- 最新
- 精选
- neohope一、再梳理一下内存结构 1、内存memmgrob_t被划分为多个功能分区,每个功能分区用一个memarea_t描述 2、每个memarea_t都有一个memdivmer_t ,每个memdivmer_t 都有一个bafhlst_t数组[0-51] 3、每个bafhlst_t都有一个链表,用于存放内存段,规则为: bafhlst_t数组中的每个bafhlst_t,会根据其在数组中的序号n,存放全部2的n次方的连续页面,也就是说: 第0个bafhlst_t,存放全部长度为1的内存段 第1个bafhlst_t,存放全部长度为2的内存段 第2个bafhlst_t,存放全部长度为4的内存段 ...... 4、在内存段处理时,将开始的msadsc_t指向了最后msadsc_t结构,内存段的起止就都清晰了,而且无论首尾,都记录了分配情况,方便各类操作 5、所以从设计层面来讲,页面的分配和释放,也一定只会是2的n次方大小 二、申请 1、根据类型找到内存区,也就是定位到了memarea_t->memdivmer_t->bafhlst_t数组 2、根据申请内存大小,用二进制1的查找,确定要至少要从bafhlst_t数组中的哪个bafhlst_t申请,才能得到足够大的内存 3、从第一个合适的bafhlst_t到最大的bafhlst_t,依次判断链表中有没有可用内存段,一旦有可用的内存段就使用 4、如果内存段大于所需,就要把多出来的内存不断除以2挂载到上一个bafhlst_t,直到达到所需长度 5、设置内存段状态,起始msadsc_t都标记为已占用 6、更新各层结构相关信息,内存申请结束 7、代码中还有各种加锁解锁,各种校验,还有从大到小申请的一种方式,可以看下 三、释放 1、根据要释放内存段的msadsc_t,获取属于哪个内存区,也就是定位到了memarea_t->memdivmer_t->bafhlst_t数组 2、根据释放内存大小,用二进制1的查找,确定最大可以释放到bafhlst_t数组中的哪个bafhlst_t,避免内存碎片化 3、设置内存段状态,起始msadsc_t都标记为未使用 4、从找到的第一个bafhlst_t到最大的bafhlst_t,依次去看链表中有没有内存段是挨着的,如果有就合并,再去下一个bafhlst_t继续合并 一旦某个bafhlst_t中没能合并,就可以退出了,因为我们只存2的n次方大小的内存段 而且每次合并内存段后,都要清理多余的标记,而且开始的msadsc_t要指向最后的msadsc_t 5、把最终合并后的内存段,加入到对应的bafhlst_t中,重新设置内存段的起始msadsc_t标记 6、更新好各层结构相关信息,内存释放结束 7、代码中还有各种加锁解锁,各种校验,可以看下 四、对于最后的问题 其实无论采用哪种分配方式,内存的碎片化都是难以彻底避免的。无论是操作系统、虚拟机还是应用,都要面对这个问题。业界有多种思路来解决或缓解此问题: 1、把不可移动内存单独管理,系统内存分区其实在一定程度上解决了这些问题 2、linux采用了 buddy system来缓解内存碎片问题,本节已经介绍 3、linux中为了处理特别小的内存请求,引入了slab技术,来辅助buddy system 4、windows有一种LFH技术,在程序启动时,会额外分配一定的连续内存给这个进程备用,从而降低系统层面的内存管理负担 5、windows在进程退出后,不会立即释放dll文件相关内存,一方面提速,一方面也缓解了操作系统内存管理负担。其实,看下你手机的APP,切换到后台时,就是这个效果 6、无论是linux还是windows都有低优先级进程,在后台默默做着内存规整工作,类似于磁盘碎片清理 7、JVM虚拟机,GC时会通过标记-整理(比如CMS)或复制-清除(比如G1)的方法来解决部分碎片问题 8、类似与LFH,可以考虑在内存分配时额外预留一部分,下次分配在预留的地方继续分配 9、为了内存规整方便,可以考虑靠近应用已分配内存区域进行分配 10、还有一种思路,就是将不连续的内存,转换为逻辑上连续的内存,来绕过碎片化问题,但一些情况下性能难以保证 应用层面也有工作能做: 1、比如redis在处理内存的时候,申请时会额外申请一部分先备着【记得是jemalloc】,释放时也不会立即释放,有单独的线程进行处理,在应用层面去降低系统内存管理负担 2、同时,redis在数据结构上也做了很多努力 3、在写程序的时候,尽量不要零零散散的去申请大量小内存; 4、除了标准库以外,可以试一下 jemalloc或Doug Lea's malloc 5、感兴趣可以看下redis内存管理的代码 额,好像跑题了。。。
作者回复: 老铁的梳理,我只能大写66666
2021-06-20450 - pedro分配的时候,如果是多个内存页面,优先向数组后面寻找,即多个连续的内存页,这就能保证分配的页面是连续的,释放的释放,组合多个页面,保证下次分配时候的连续性。 页大小是 4KB,对于小对象的分配,这样是非常浪费的,每开辟一个小对象都要申请一个物理页,这谁受得了啊,所以啊,linux 提出了 slab 分配算法。
作者回复: 下节课会讲的
2021-06-1826 - Fan在内存页面分配过程中,是怎样尽可能保证内存页面连续的呢? 1 .先找到能满足的需要的内存 在m_mdmlielst中的位置。 2.如果内存段大于所需,就要把多出来的内存不断除以2挂载到上一个bafhlst_t,直到达到所需长度。 例如 需要9k内存: 先定位到第 4 个 bafhlst_t 结构中的 4 个连续的 msadsc_t 结构16k(4K*4) 。 分出9K 内存,往上一个bafhlst_t挂载,直到把剩余的内存挂载完。 16K --> 9K 4K 2K 1K
作者回复: 66666
2021-06-2933 - 吕默看代码完全看不懂,但是看图马上懂的不行——天生架构师。。。。。。。
作者回复: 哈哈 你写ppt一定很厉害了
2021-09-102 - 沈畅为什么分配pages,只设置链表首尾结构的属性,中间结构不用设置吗?这里不太理解 比如分4个页面,只设置msadsc[0] masadsc[3]的属性吗?
作者回复: 中间和首尾是一个整体 所以不需要设置
2021-09-071 - 朱熙这节课讲的基本是buddy吧?下面对于小对象就是slab了
作者回复: Linux的伙伴系统在后面会讲的
2021-06-181 - blentle回答一下思考题,是不是有个单独的线程或者每次释放内存后开个守护线程进行碎片整理和移动来实现. 还有这节课的代码真的好硬核,需要好长时间消化
作者回复: 你好,确实可以 这样做
2021-06-181 - Han有个疑问,比如第一次申请9k,第二次申请2k,第三次释放9k 第一次:按照文章中的算法,有7k放到前面的bafhlst_t中:16K --> 9K 4K 2K 1K 第二次:2k的bafhlst_t被占用 第三次:释放至1k位置,2k的bafhlst_t被占用,算法结束,后面的4k就没有被回收了?
作者回复: 666666666
2022-07-212 - 艾恩凝打卡,代码全部分析完了,分配的时候,我发现并没有把bafhlst_h 中的af_frelst 这个链表中空闲的 转到 af_alclst中去,释放的时候这个af_alclst 也没用到,那这不是白定义了 ,代码解释太粗糙,还是自己慢慢来看代码。用导图再重新梳理一遍,查漏
作者回复: 不是白定义 是算法没完全实施 在课程的代码中 没用到而已
2022-04-14 - 沈畅花了三天时间对着代码把物理内存管理内容看完了。收益很大。虽然还有一些字段不明白意思,后续继续研究。这里有个疑问,在释放内存pages时,需要进行page的合并,遍历bafhlst_t数组,这样会不会性能上比较低?
作者回复: 不会的 这个数组不会动态增加 也不是全部遍历
2021-09-08
收起评论