操作系统内核的几大模块:
进程调度是核心模块,本系列博客的大部分内容都在讲解进程调度模块。从本篇开始讲解内存管理,可能分成两篇来讲解。
加过内存条吗?内存条长啥样?
内存条就长这样,当然还有一个问题:
磁盘长啥样?现在主要有两种:SSD(固态)和HHD(机械)
左边的是固态(笔者在更换大容量SSD的时候拍的),右边的是机械,机械硬盘俗称自行车(因为比较慢)。
现在的SSD降价很厉害,所以很多童鞋们都准备丢掉自行车啦!
好了,不闲扯了,步入正题
下面以提问-回答的方式来思考如何分配物理内存!
首先要考虑一个问题,能不能把所有内存都给用户?
课外充电站:
Linux0.11版内核中一共可以管理16M内存,其中0-1M给内核使用,其余才能分配给用户
既然内存分成两部分:内核使用部分、用户使用部分,那么它们是否是平等的呢?
再来一个问题:我们如何记录哪些内存已经分配/未分配呢?这些信息保存在哪里?
有了上述问题,我们开始研究内存分配方案。
有以下几种物理内存分配方案:
我们本应该先讲分段再讲分页,但是连续分配和分页机制是思维的一个转变,具有衔接性,所以我们讲完连续分配就讲分页管理。
连续分配存储管理可以再细分成如下几类:
单一连续分配是最简单的管理方式:
举个例子:
优点:
优点:
缺点:
是上述方案的改进版本,分区大小是可变的,这种方案最致命的缺点是容易形成小碎片,至于碎片如何形成,下面举例讲解。
这里的可变分区看似很灵活,比如某进程需要10M内存,那么就分配给它10M内存,这种灵活的背后隐藏着问题:
如何分配内存以及小碎片如何形成:
所以我们需要引入额外的机制来解决上述的小碎片问题:
首先我们思考能不能改进空闲内存的分配方案,从而减缓小碎片问题,思考如下几种分配方案:
看完了上述分配方案,其实不难发现,别管使用哪种方案,都无法避免碎片的产生,所以继续研究新的机制。
既然碎片无法避免,那就想办法将碎片集中起来:
由于目前操作系统并不采用可变分区管理方式,所我们不深究上述机制,不过可以简单想想:紧凑操作不是那么容易,需要移动其他进程的内存空间,可能会带来新的问题。
优点:
缺点:
连续分配在嵌入式设备中有用武之地,现代操作系统没有采用连续分配方案。
上面我们讲了连续分配方案,也阐明了连续分配方案的缺点,下面我们针对连续分配的缺点继续思考:
我们一步一步地思考出了如何克服碎片化问题,重要的思想:将内存划分成一小块、一小块,然后离散管理。
上述思想很重要,以后还会用到,即离散化克服碎片问题(因为碎片就是离散的,干脆完全离散分配,那么碎片问题就解决了)
既然要将内存的分配离散化,那么我们先来直观的看下离散化后的场景:
本来代码是连续分布在内存中的,现在离散化了,每段代码都离散的分布在各个角落。
问题来了:代码的分布都这么散乱,怎么管理呢?
讲解上述问题之前先补充下几个概念:
先思考一个简单的问题:
还会引出一个问题:页面是逻辑地址的表述,页框是物理地址的表述,那么两者如何转换呢?两者的对应关系是什么?
现在我们来回答上面提到的问题:
代码的分布那么散乱,怎么管理呢?
其实思想也很简单:图书馆那么多书,也是离散的分布在各个楼层的各个角落,怎么管理的?
再来确定下我们需要的数据结构:
向上滚动一下,再看下上面的图:
设计出了这种方案,那么代码怎么执行呢?
这样我们的数据结构就设计出来了,而且已经验证过,程序可以运行,那么我们思考:数据结构就这么简单吗?还需要其他信息吗?
该程序页数:100000/1000=100页
逻辑地址1002的物理地址:
逻辑地址4010的算法类似。
我们将上述的计算过程一般化:
上面提到了逻辑地址,那么逻辑地址长啥样?
我们结合上图来分析:取一个逻辑地址里的操作数的过程:
即上述方案方案了两次内存,会影响效率。
我们先做下简单的运算:
上面的计算可能优点乱,需要仔细看下,计算的目的是为了明确:
我们的一个页表装在了1024个页框里如下图所示:
这个页表实在是太大了,其需要1024个页框才能装下,即4M的内存空间。
页表太大会带来什么问题?
举个例子来说明上述问题:
多级页表能减少每个进程需要保存的信息
如上图所示:每个进程中只需要保存一个4KB大小的一级页表,用来记录1024个二级页表中哪个用到,哪个没有用即可,要比保存4MB大小的所有二级页表划算的多。
使用了二级页表的管理机制以后,32位逻辑地址应该如何划分呢?我们需要做下计算:
先介绍一个寄存器CR3:
地址转换方式:
Linux0.11版页初始化源码解析+虚拟存储管理+分段存储管理放在下一篇博客,这篇太长了。
如果觉得写的不错,对读者有帮助,可以给笔者点个赞,鼓励一下哦~
本系列博客目录
下一篇:内存管理续