操作系统是一门比较难啃的课程,同时操作系统知识对开发者们来说是十分重要,相信各位在学操作系统的时候,有太多的抽象难以理解的词汇与概念,把我们直接劝退,即使怀着满腔热血的心情学操作系统,不到 3 分钟睡意就突然袭来。
所以本人想把自己的想法通过图解+大白话的形式,产出操作系统系列文章,让小白也能看懂,帮助大家快速科普入门
本篇开始介绍内存,内存在操作系统中还是比较重要的,理解了它,对整个操作系统的工作会有一个初步的轮廓。
内容大纲
什么是内存
小故事
我们想去摆地摊(准备运行程序进程)需要经过那几 个步骤,这里猜测一下。
首先要去申请摊位(申请内存),(操作系统)根据现在剩余的地毯空间与你地毯的规模划分一块相应大小的摊位(内存)给你,接着你就可以愉快的摆摊(运行程序进程)赚钱啦。
也会时不时的来检查(整理内存空间碎片),摊位是否规整,有没有阻碍正常的人行道。
简而言之,电脑上的程序(进程)运行是需要使用到对应大小的物理内存。
虚拟内存
由此我们引出了两个概念:
- 进程中使用的内存地址叫虚拟地址
- 存在计算硬件里的空间地址叫物理地址
主要有三种方式,分别是分段、分页、段页,下面我们来看看这三种内存管理方式
内存分段
程序包含若干个逻辑分段,如可由代码段、数据段、栈段、堆段组成,每个分段都有不同的属性,所以内存以分段的形式把这些段分离出来进行管理
接下来对这两个问题进行分析
分段方式是如何产生内存碎片的?
在说内存碎片之前,还是先弄明白,什么是内存碎片?,8个人去外面吃饭,因为饭点原因,人比较多,剩下的都是4人小餐桌,这些4人小餐桌就是我们所说的内存碎片,此时会有小伙伴说,把2个4人小餐桌拼凑在一起就解决了这个问题,非常简单,我们把这种方式称为内存碎片整理(涉及到内存交换)。
回到正题,我们来看一例子,假设物理内存只有1GB (1024MB),用户电脑上运行了多个程序:
- 浏览器占用128MB
- 音乐软件占用256MB
- 游戏占用了512MB
这个时候我们关闭浏览器,剩余物理内存1024MB -(256MB+512MB)= 256MB。但是这剩余的256MB物理内存不是连续的,被分为了两段128MB,导致没有空间再打开一个200MB的程序,如下图所示
这里的内存碎片问题共有两点:
- 外部内存碎片,就是多个不连续的小物理内存空间,导致新的程序无法被装载
- 内部内存碎片,程序所有的内存都被装载进了物理内存,但是程序有部分的内存,可能不经常使用,造成内存的浪费
解决外部内存碎片的方法就是使用内存碎片整理
内存碎片整理通过内存交换的方式来实现,我们可以把音乐软件占用的256MB加载到硬盘上面去,再从硬盘读取回来,但是读取回来的位置不再是原来的位置,而是紧跟已经占用的游戏512MB后面,这样两个128MB的空闲物理内存就合并成了一个256MB的连续物理内存,于是新的200MB新程序就能被装载进来
内存交换空间,在 Linux 系统里,是我们常看到的 Swap 空间,这块空间是从硬盘划分出来的,用于内存与硬盘的空间交换。
分段方式为什么内存交换效率低?
首先分段管理容易造成内存碎片,导致内存交换的频率较高,因为硬盘的访问速度比内存慢太多了,然后每次交换的时候,把一大段连续的内存写入到硬盘,再又从硬盘读取出来,如果交换的是一个占内存空间很大的程序,这样整个机器都会显得卡顿,过程也很慢的,所以说分段方式内存交换效率低。
为了解决内存分段管理造成的内存碎片与内存交换效率低的问题,就出现了内存分页
内存分页
分段的好处是能产生连续的内存空间,但是会出现大量内存碎片与内存交换效率低的问题
先思考一下怎么解决这两个问题,内存碎片是由多个不连续的小物理内存空间造成,如果把这些不连续的小物理内存空间组合起来,是不是解决了这个问题?同样的,内存交换的时候我们保证交换的数据小,是不是能提高内存交换的效率?
分页方式是如何解决内存碎片与内存交换效率慢的问题呢?
内存碎片的解决:
内存交换效率慢的解决:
之前说过,减少交换数据的大小,可以提高内存交换效率,分页方式是这样解决的,如果内存空间不够时,操作系统会把其他正在运行的进程中的「最近没被使用」的内存页释放掉,也就是加载到硬盘,称为换出,一旦需要的时候再加载进来,称为换入。所以一次性写入硬盘的也只有一个页或几个页,内存的交换效率自然就提升了。
分页方式使加载程序的时候,不再需要一次性都把程序加载到物理内存中。完全可以在进行虚拟内存和物理内存的页之间的映射之后,并不真的把页加载到物理内存里,而是只有在程序运行中,需要用到对应虚拟内存页里面的指令和数据时,再加载到物理内存里面去(用大白话说,当你需要用到的时候才会去使用对应的物理内存)。
就是下面这几步
- 页号找到页表中的页项
- 获取页项的物理页号基地址
- 偏移量+物理页号基地址计算出物理内存地址
是不是非常的简单,但是这种分页方式使用到操作系统上会不会问题呢?那必然是会有问题的,还记得之前提到的每个进程会分配一个页表嘛?下面来为大家解开这个伏笔
在分页方式下,每个进程分配一个页表会有什么问题?
不卖关子了,每个进程分配一个页表会有空间上的缺陷,因为操作系统上可以运行非常多的进程,那不就意味着页表数量非常多!
1B(Byte 字节)=8bit,
1KB (Kilobyte 千字节)=1024B,
1MB (Megabyte 兆字节 简称“兆”)=1024KB,
1GB (Gigabyte 吉字节 又称“千兆”)=1024MB
4MB看起来不大,但是数量上来了就很恐怖了,假设 100 个进程的话,就需要 400MB 的内存来存储页表,这是非常大的内存了,更别说 位的环境了。
为了解决空间上的问题,在对分页方式的基础上,进行优化,出现了多级页表方式
多级页表
在前面我们知道了,分页方式在32位环境下,以每页4KB来计算,一共有100万页,「页表项」需要 4 个字节大小来存储,一个页表包含100万个「页表项」,那么每个进程的页表需要占用4MB大小,多级页表要如何解决这种问题呢?
在页表的基础上做一次二级分页,把100万「页表项」分为一级页表「1024个页表项」,「一级页表项」下又关联二级页表「1024个页表项」,这样一级页表的1024个页表项就覆盖到了4GB的空间范围映射,并且二级页表按需加载,这样页表占用的空间就大大降低。
做个简单的计算,假设只有 20% 的一级页表项被用到了,那么页表占用的内存空间就只有 4KB(一级页表) + 20% * 4MB(二级页表)= 0.804MB,这对比单级页表的 4MB 是不是一个巨大的节约?
- 全局页目录项
- 上层页目录项
- 中间页目录项
- 页表项
[图片上传失败...(image-8d5ffc-11456087631)]
<figcaption>在这里插入图片描述</figcaption>
TBL
程序是局部性的,即在一段时间内,整个程序的执行仅限于程序的某一部分。相应的,执行所访问的存储空间也局限于某个内存区域。
操作系统就利用这一特性,把最多使用的几个页表项放到TBL缓存, CPU 在寻址时,会先查 TLB,如果没找到,才会继续查常规的页表,TLB 的命中率其实很高的,因为程序最常访问的页就那么几个。
内存段页
段式与页式并不是相对的,他们也可以组合在一起使用,在段的基础上进行分页分级
- 先将程序划分为多个有逻辑意义的段,也就是前面提到的分段机制
- 接着再把每个段划分为多个页,也就是对分段划分出来的连续空间,再划分固定大小的页
就是下面这几步
- 通过段号获取段表的段项
- 通过段项获取到页表地址
- 通过页表地址找到段页表
- 通过段内页号找到段页表的段页项
- 通过段页项获取物理页基地址
- 通过物理页基地址+偏移量计算出物理内存地址
总结
启用大量进程造成内存紧张不足的时候,操作系统会通过内存交换技术,把不常使用的内存加载到硬盘(换出),使用时从硬盘加载到内存(换入)
相关资源: