操作系统实战 45 讲
彭东
网名 LMOS,Intel 傲腾项目关键开发者
65203 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 60 讲
尝尝鲜:从一个Hello到另一个Hello (2讲)
特别放送 (1讲)
操作系统实战 45 讲
15
15
1.0x
00:00/00:00
登录|注册

19 | 土地不能浪费:如何管理内存对象?

你好,我是 LMOS。
在前面的课程中,我们建立了物理内存页面管理器,它既可以分配单个页面,也可以分配多个连续的页面,还能指定在特殊内存地址区域中分配页面。
但你发现没有,物理内存页面管理器一次分配至少是一个页面,而我们对内存分页是一个页面 4KB,即 4096 字节。对于小于一个页面的内存分配请求,它无能为力。如果要实现小于一个页面的内存分配请求,又该怎么做呢?
这节课我们就一起来解决这个问题。课程配套代码,你可以从这里获得。

malloc 给我们的启发

首先,我想和你说说,为什么小于一个页面的内存我们也要格外珍惜?
如果你在大学学过 C 程序设计语言的话,相信你对 C 库中的 malloc 函数也不会陌生,它负责完成分配一块内存空间的功能。
下面的代码。我相信你也写过,或者写过类似的,不用多介绍你也可以明白。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char *str;
//内存分配 存放15个char字符类型
str = (char *) malloc(15);
if (str == NULL) {
printf("mem alloc err\n");
return -1;
}
//把hello world字符串复制到str开始的内存地址空间中
strcpy(str, "hello world");
//打印hello world字符串和它的地址
printf("String = %s, Address = %u\n", str, str);
//释放分配的内存
free(str);
return(0);
}
这个代码流程很简单,就是分配一块 15 字节大小的内存空间,然后把字符串复制到分配的内存空间中,最后用字符串的形式打印了那个块内存,最后释放该内存空间。
但我们并不是要了解 malloc、free 函数的工作原理,而是要清楚,像这样分配几个字节内存空间的操作,这在内核中比比皆是。

页还能细分吗

是的,单从内存角度来看,页最小是以字节为单位的。但是从 MMU 角度看,内存是以页为单位的,所以我们的 Cosmos 的物理内存分配器也以页为单位。现在的问题是,内核中有大量远小于一个页面的内存分配请求,如果对此还是分配一个页面,就会浪费内存。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了内存对象管理的重要性以及如何解决物理内存页面管理器一次分配至少一个页面的限制。作者首先强调了对小于一个页面的内存分配请求的重要性,并提出了将一个页面或连续多个页面分成不同大小的内存对象的概念。文章详细介绍了内存对象和内存对象容器的设计,包括相应的数据结构和代码示例。通过代码示例展示了内存对象容器、扩展内存和管理内存对象容器占用的物理内存页面之间的关系。此外,文章还介绍了内存对象的分配接口和查找内存对象容器的过程。总体而言,本文提供了清晰的技术指导,对于需要深入了解内存对象管理的读者来说,是一篇非常有价值的文章。 文章内容涵盖了内存对象容器的建立和扩容,以及相关的数据结构和代码示例,为读者提供了全面的技术指导。此外,文章还强调了内存对象容器的销毁过程,以及内存对象管理的重要性。通过对内存对象的分配和释放过程的详细讲解,读者可以深入了解内存对象管理的实现细节。整篇文章以技术性强、逻辑清晰为特点,对于需要深入了解内存对象管理的读者具有较高的参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《操作系统实战 45 讲》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(23)

  • 最新
  • 精选
  • 听水的湖
    置顶
    我是专栏编辑小新。缩写的代码注释补充如下: bafhlst=block alloc free head list freobjh=free object head kmbext=kernel memory block extend kmsobmgrhed=kernel memory space object manager head onmsz=on memory size kmsob_new_opkmsob=..._Operation 【提示】LMOS不推荐大家通过阅读代码了解原理,更推荐结合课程内容和关键注释学习,先关注关键函数的功能,命名并不重要。

    作者回复: 对 对 对

    2022-12-06归属地:湖北
    2
  • 沈畅
    置顶
    建议大家先浏览文稿,理解数据结构以及整体的设计思路(作者画的数据结构图要了然于胸)。先把数据结构理顺,再细看代码就比较容易了。我一开始也是觉得比较难懂,确实变量和函数名缩写不是很好。但是只要理解文稿中概念,和数据结构对应上,不要陷入这些奇怪的命名,代码思路就清晰了。先不要扣细节,从框架到函数最后到细节。看完还是很有收获的。其实看多了,有些缩写也大概猜得出来。

    作者回复: 对头铁子

    2021-09-14
    2
  • wenkin
    置顶
    Linux内存管理之slab分配器分析: 前面分析过了大内存分配的实现机制,事实,上,若为小块内存而请求整个页面,这样对于内存来说是种极度的浪费。因此linux采用了slab来管理小块内存的分配与释放。 1:内核函数经常倾向于反复请求相同的数据类型。比如:创建进程时,会请求一块内存来存放mm结构。 2: 不同的结构使用不同的分配方法可以提高效率。同样,如果进程在撤消的时候,内核不把mm结构释放掉,而是存放到一个缓冲区里,以后若有请求mm存储空间的行为就可以直接从缓冲区中取得,而不需重新分配内存. 3:前面,伙伴系统频繁分配,释放内存会影响系统的效率,以此,可以把要释放到的内存放到缓冲区中,直至超过一-个阀值才把它释放至伙伴系统,这样可以在- -定程度.上缓减减伙伴系统的压力。 4:为了缓减“内碎片”的产生,通常可以把小内存块按照2的倍数组织在一起,这一点和伙伴系统类似,这就是为什么分配内存对象大小时要按照 Cache 行大小的倍数分配。 笔记: 本章内容可以细分为三个小节即:内存的初始化,内存的分配,内存的释放,其具体流程参考neohope的笔记,就不多赘述。

    作者回复: 学的很透

    2021-09-08
    6
  • neohope
    置顶
    一、整理一下内存结构 1、memmgrob_t中有kmsobmgrhed_t 2、kmsobmgrhed_t中有一个koblst_t数组【KOBLST_MAX个】,序号为n的koblst_t,存储全部实际长度为长度为32*(n+1)内存对象 3、每个koblst_t,都包括一个kmsob_t链表 4、kmsob_t结构如下: 结构体描述部分【 双向链表 扩展结构链表【kmbext_t】 空闲对象链表【freobjh_t】 已分配对象链表【freobjh_t】 占用内存页面管理结构【msomdc_t】 kmsob_t结构体页面链表【so_mc.mc_kmobinlst】 全部kmbext_t结构体页面链表【so_mc.mc_lst】 结构体起止地址 ...... 】 除结构体描述部分,都按相同大小划分为了内存对象【freobjh_t】 5、扩展管理kmbext_t,用于扩容 结构体描述部分{ 双向链表 结构体起止地址 ...... } 除结构体描述部分,都按相同大小划分为了内存对象【freobjh_t】 6、链表处理部分做的真漂亮! 二、分配内存 1、从memmgrob_t获取kmsobmgrhed_t,也就找到了koblst_t数组 2、根据申请内存对象大小,找到对应的koblst_t【第一个内存对象比需求大的koblst_t】 3、如果koblst_t中没有找到kmsob_t,则要初始化 A、按页申请内存【1、2或4页】 B、进行kmsob_t初始化工作,首先初始化描述部分 C、将之后的空间,按固定大小全部初始化为freobjh_t结构 D、把全部freobjh_t挂载到koblst_t的空闲列表中 E、然后将kmsob_t挂载到koblst_t结构中去 4、在kmsob_t中分配内存对象 4.1、首先判断kmsob_t是否有空闲对象可以分配 4.2、如果没有空闲对象可以分配,则尝试扩容,创建新的kmbext_t:: A、申请内存【1、2或4页】 B、并进行初始化工作kmbext_t,首先初始化描述部分 C、将之后的空间,按固定大小全部初始化为freobjh_t结构 D、把内存页面记录到kmsob_t的页面列表中 E、把freobjh_t挂载到koblst_t的空闲列表中 F、把kmbext_t挂载到kmsob_t的扩展结构链表中 4.3、最后返回一个空闲内存对象,并从空闲列表中移除 5、更新kmsobmgrhed_t结构的信息 6、代码中还有各种加速,加锁解锁、校验代码,可以看下 三、释放内存 1、从memmgrob_t获取kmsobmgrhed_t,也就找到了koblst_t数组 2、根据申请内存对象大小,找到对应的koblst_t【第一个内存对象比需求大的koblst_t】 3、查找内存对象所属的kmsob_t结构 对于koblst_t中的每一个kmsob_t结构: A、先检查内存对象的地址是否落在kmsob_t结构的地址区间 B、然后依次检测内存对象的地址是否落在kmsob_t的各个kmbext_t扩展结构的地址区间 4、释放内存对象,也就是将内存对象添加到空闲列表中 5、尝试销毁内存对象所在 kmsob_t结构 4.1、首先判断该kmsob_t全部内存对象都已释放 4.2、如果全部内存对象都已释放,则释放kmsob_t A、将kmsob_t脱链 B、更新kmsobmgrhed_t结构的信息 C、遍历释放kmsob_t中全部扩展结构占用的内存页面【先脱链,再释放】 D、释放kmsob_t自身占用的全部页面【先脱链,再释放】 6、代码中还有各种加速,加锁解锁、校验代码,可以看下 最后,想问一下,koblst_t在什么情况下,会挂载多个kmsob_t呢?感觉内存对象不够用,就去补充kmbext_t了吧?

    作者回复: 大佬 66666 最后,想问一下,koblst_t在什么情况下,会挂载多个kmsob_t呢?感觉内存对象不够用,就去补充kmbext_t了吧? ----------------------------------------------------- 目前不会,因为还没有 区分 kmsob_t的类型

    2021-06-21
    2
    19
  • pedro
    置顶
    怕大家误会我的意思,我再说明一下。我上面的评论其实是在呼吁大家不要拘泥于代码,也不要深陷细节,一叶而障目是学习路上最容易犯的错误。 我以自身为例来说,初学数据结构时,链表是最简单的一种数据结构,老师一讲就懂,书一看就会,可是自己去写,尤其是链表的各种操作都是细节满满,很考验个人代码功底和思维完善性,至今我都记得从我懂链表起,到真正写出一个能work的链表都花了一周时间,最开始写是CV,后面是背,多次画图和理解后我才能闭卷完完整整的写出链表。 虽然这过去了好几年,我也从初学者到现在走了很多路。对于这几节专栏而言,同样如此,无论是上一节的伙伴算法还是这一节的slab,都是linux系统走过了几十年迭代,摸爬滚打过滤下的精华。这样的算法都有一个普遍性,那就是思路和方式听起来都简单,可是自己去实现却又困难重重,所以专栏里面出现了大量的代码,大家都不适应,扣代码细节,反而忘记了buddy和slab要解决的问题,要干什么,为什么这样设计,这才是第一次读专栏的重点。 代码我也没全读懂,代码我也看着头疼,所以我才自勉写下上面的评论,希望大家能跳出来,宏观的看待问题,因为即使是linux本人都未必能扣出细节。大家加油~

    作者回复: 666666

    2021-06-21
    3
    38
  • 艾恩凝
    为啥不能理解细节代码,强迫症,整体流程明白了,深究代码,结束了之后,再画一遍流程图,加深印象,虽然慢倒也可以,对每个函数 函数参数 步骤 添加注释,笔记+流程图 --->https://aeneag.xyz/virginOS 打卡

    作者回复: 我要分享

    2022-04-20
    5
  • pedro
    评论越来越少,证明能跟上的人也越来越少。 作为一个每节都评论,都思考的人,我也能感受到内容难度的加大,而且缺乏阅读方向,代码量越来越大,但是文章要解决什么问题,思路是什么,为什么要这么解决,为什么不用流程图画一下等等,都能改善专栏阅读的困难。 至于思考题,那就比较简单了,前面的小节也谈过cpu cache line的问题,总而言之分配内存对象大小按照cache行来分配根本原因在于合理利用cpu缓存。

    作者回复: “文章要解决什么问题,思路是什么,为什么要这么解决” 文章中都有啊 ,页面分配都懂了, 这个应该很简单才对,有什么地方觉得很难的

    2021-06-21
    5
    5
  • 朱熙
    感觉确实如pedro所说,能跟上的人越来越少,可能也和内核越来越深入有关系。 个人想法是不用纠结代码的每一行,更多的跟着注释体会代码整体流程,知道每块代码在做什么,如果有必要,再去看每行代码。 也希望将来能够每个模块与linux内核做一个比较,讲解下优缺点或者差异,让读者能够简单了解linux为了某些目的,增加了那些逻辑。

    作者回复: 对的

    2021-06-21
    4
  • 飘在空中的鱼
    老师,这个变量名看得真是头疼啊

    作者回复: 哈哈 看注释

    2022-05-06
    2
  • blackonion
    一开始瞄了一眼发现这节代码比较多,的确有点发怵,但看了评论,知道不抠名字这个细节比较好后,耐下心来看了一遍,基本还是能懂的。老师的图非常给力,行文的思路也清晰,注释也比较到位。看到半年过去了,老师还会回答新读者的提问,这种程度的用心真的让我感动~ 用我自己的话总结一下这节内容吧。 为提高内存利用率,避免需要很少内容时分配一页,引入了内存对象。 一个结构含有一组内存对象,这个结构叫内存对象容器。 内存对象容器加上内存对象数组默认占据一页,如果不够的话可以申请新的页,用一个类似的结构管理扩展的内存对象。 一种内存对象容器对应一种特定大小的内存对象。 有个结构含有一组内存对象容器,姑且叫内存对象容器管理器。 初始化内存管理功能时,也有初始化内存对象容器管理器的数组。但内存对象是有需求才分配的。

    作者回复: 对的

    2021-12-26
    2
收起评论
显示
设置
留言
23
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部