共计 4984 个字符,预计需要花费 13 分钟才能阅读完成。
前言
操作系统是一门比拟难啃的课程,同时操作系统常识对开发者们来说是非常重要,置信各位在学操作系统的时候,有太多的形象难以了解的词汇与概念,把咱们间接劝退,即便怀着满腔热血的情绪学操作系统,不到 3 分钟睡意就忽然袭来。
所以自己想把本人的想法通过图解 + 大白话的模式,产出操作系统系列文章,让小白也能看懂,帮忙大家疾速科普入门
本篇开始介绍内存,内存在操作系统中还是比拟重要的,了解了它,对整个操作系统的工作会有一个初步的轮廓。
内容纲要
内容纲要
注释
什么是内存
小故事
咱们想去摆地摊(筹备运行程序过程)须要通过那几 个步骤,这里猜想一下。
首先要去城管申请摊位(申请内存),城管(操作系统)依据当初残余的地毯空间与你地毯的规模划分一块相应大小的摊位(内存)给你,接着你就能够欢快的摆摊(运行程序过程)赚钱啦。
城管也会时不时的来查看(整顿内存空间碎片),摊位是否规整,有没有妨碍失常的人行道。
简而言之,电脑上的程序(过程)运行是须要应用到对应大小的物理内存。
虚拟内存
实际上运行的过程并不是间接应用物理内存地址,而是把过程应用的内存地址与理论的物理内存地址做隔离,即操作系统会为每个过程调配独立的一套「虚拟地址」。
每个过程玩本人的地址,互不干涉,至于虚拟地址怎么映射到物理地址,对过程来说是通明的,操作系统曾经把这些安顿的明明白白了。
操作系统会提供一种机制,将不同过程的虚拟地址和不同内存的物理地址映射起来,如下图所示
在这里插入图片形容
由此咱们引出了两个概念:
- 过程中应用的内存地址叫 虚拟地址
- 存在计算硬件里的空间地址叫 物理地址
简略来说操作系统引入虚拟空间,过程持有的虚拟地址会通过 CPU 芯片中的内存治理单元(MMU)的映射关系,来转换成物理地址,再通过物理地址拜访物理内存
操作系统是如何治理虚拟地址与物理内存地址之间关系?
次要有三种形式,别离是 分段、分页、段页,上面咱们来看看这三种内存治理形式
内存分段
程序蕴含若干个逻辑分段,如可由代码段、数据段、栈段、堆段组成,每个分段都有不同的属性,所以内存以分段的模式把这些段分离出来进行治理
在内存分段形式下,虚拟地址和物理地址是如何映射的?
分段治理下的虚拟地址由两局部组成,段号和段内偏移量
在这里插入图片形容
- 通过段号映射段表的项
- 从项中获取到段基地址
- 段基地址 + 段内偏移量 = 应用的物理内存
通过上述晓得了,应用段号去映射段表的项,应用项中的段基地址与偏移量计算出物理内存地址,但实际上,分段形式会把程序的虚拟地址分为 4 段,每个段在段表中有一个项,在这一项找到段的基地址,再加上偏移量计算出物理内存地址
分段的形式,很好的解决了,程序自身不须要关怀具体物理内存地址的问题,然而它仍有不足之处:
- 内存碎片的问题
- 内存替换的效率低的问题
接下来对这两个问题进行剖析
分段形式是如何产生内存碎片的?
在说内存碎片之前,还是先弄明确,什么是内存碎片?,8 集体去里面吃饭,因为饭点起因,人比拟多,剩下的都是 4 人小餐桌,这些 4 人小餐桌就是咱们所说的内存碎片,此时会有小伙伴说,把 2 个 4 人小餐桌拼凑在一起就解决了这个问题,非常简单,咱们把这种形式称为内存碎片整顿(波及到内存替换)。
回到正题,咱们来看一例子,假如物理内存只有 1GB(1024MB),用户电脑上运行了多个程序:
- 浏览器占用 128MB
- 音乐软件占用 256MB
- 游戏占用了 512MB
这个时候咱们敞开浏览器,残余物理内存 1024MB -(256MB+512MB)= 256MB。然而这残余的 256MB 物理内存不是间断的,被分为了两段 128MB,导致没有空间再关上一个 200MB 的程序,如下图所示
在这里插入图片形容
这里的内存碎片问题共有两点:
- 内部内存碎片,就是多个不间断的小物理内存空间,导致新的程序无奈被装载
- 外部内存碎片,程序所有的内存都被装载进了物理内存,然而程序有局部的内存,可能不常常应用,造成内存的节约
解决内部内存碎片的办法就是应用内存碎片整顿
内存碎片整顿通过内存替换的形式来实现,咱们能够把音乐软件占用的 256MB 加载到硬盘下面去,再从硬盘读取回来,然而读取回来的地位不再是原来的地位,而是紧跟曾经占用的游戏 512MB 前面,这样两个 128MB 的闲暇物理内存就合并成了一个 256MB 的间断物理内存,于是新的 200MB 新程序就能被装载进来
内存替换空间,在 Linux 零碎里,是咱们常看到的 Swap 空间,这块空间是从硬盘划分进去的,用于内存与硬盘的空间替换。
分段形式为什么内存替换效率低?
首先分段治理容易造成内存碎片,导致内存替换的频率较高,因为硬盘的访问速度比内存慢太多了,而后每次替换的时候,把一大段间断的内存写入到硬盘,再又从硬盘读取进去,如果替换的是一个占内存空间很大的程序,这样整个机器都会显得卡顿,过程也很慢的,所以说分段形式内存替换效率低。
为了解决内存分段治理造成的内存碎片与内存替换效率低的问题,就呈现了内存分页
内存分页
分段的益处是能产生间断的内存空间,然而会呈现大量内存碎片与内存替换效率低的问题
先思考一下怎么解决这两个问题,内存碎片是由多个不间断的小物理内存空间造成,如果把这些不间断的小物理内存空间组合起来,是不是解决了这个问题?同样的,内存替换的时候咱们保障替换的数据小,是不是能进步内存替换的效率?
这个方法就是内存分页,分页是把整个虚构与物理空间切成一段段固定尺寸的大小,这样一个间断并且尺寸固定的空间,咱们叫页,在 Linux 下,每一页的大小为 4KB。(虚拟空间是指存储一套虚拟地址的空间)
虚拟地址与物理地址是通过页表来映射,虚拟空间内的虚拟地址肯定是间断的,物理地址不肯定,但能够通过间断的虚拟地址把多个不间断的物理内存组合应用。
在这里插入图片形容
而当过程拜访的虚拟地址在页表中查不到时,零碎会产生一个缺页异样,进入零碎内核空间调配物理内存、更新过程页表,最初再返回用户空间,复原过程的运行。
分页形式是如何解决内存碎片与内存替换效率慢的问题呢?
内存碎片的解决:
因为应用内存的单位变成固定大小的页,所以每个程序的虚拟空间保护的也是间断的页(虚拟地址),通过页表再映射到物理内存页,尽管映射的物理内存页不间断,然而虚拟空间是间断的,能够让它们组合起来应用,但这也只能解决内部内存碎片问题,没有解决外部内碎片问题,因为每页都有固定大小,可能某一页只应用了局部,仍然会造成一些节约。
内存替换效率慢的解决:
之前说过,缩小替换数据的大小,能够进步内存替换效率,分页形式是这样解决的,如果内存空间不够时,操作系统会把其余正在运行的过程中的「最近没被应用」的内存页开释掉,也就是加载到硬盘,称为换出,一旦须要的时候再加载进来,称为换入。所以一次性写入硬盘的也只有一个页或几个页,内存的替换效率天然就晋升了。
分页形式使加载程序的时候,不再须要一次性都把程序加载到物理内存中。齐全能够在进行虚拟内存和物理内存的页之间的映射之后,并不真的把页加载到物理内存里,而是只有在程序运行中,须要用到对应虚拟内存页外面的指令和数据时,再加载到物理内存外面去(用大白话说,当你须要用到的时候才会去应用对应的物理内存)。
在内存分页形式下,虚拟地址和物理地址是如何映射的?
在分页机制下,每个过程都会调配一个页表,虚拟地址会分为两局部,页号和页内偏移量,页号作为页表的索引, 页表蕴含物理页每页所在物理内存的基地址,页内偏移量 + 物理内存基地址就组成了物理内存地址,如下图所示
在这里插入图片形容
就是上面这几步
- 页号找到页表中的页项
- 获取页项的物理页号基地址
- 偏移量 + 物理页号基地址计算出物理内存地址
是不是十分的简略,然而这种分页形式应用到操作系统上会不会问题呢?那必然是会有问题的,还记得之前提到的每个过程会调配一个页表嘛?上面来为大家解开这个伏笔
在分页形式下,每个过程调配一个页表会有什么问题?
不卖关子了,每个过程调配一个页表会有空间上的缺点,因为操作系统上能够运行十分多的过程,那不就意味着页表数量十分多!
1 1B(Byte 字节)=8bit,2 1KB (Kilobyte 千字节)=1024B,3 1MB (Megabyte 兆字节 简称“兆”)=1024KB,4 1GB (Gigabyte 吉字节 又称“千兆”)=1024MB
以 32 位的环境为例,虚拟地址空间范畴共有 4GB,假如一个页的大小是 4KB(2^12),那么就须要大概 100 万(2^20)个页,每个「页表项」须要 4 个字节大小来存储,那么整个 4GB 空间范畴的映射就要有 4MB 的内存来存储页表。
4MB 看起来不大,然而数量上来了就很恐怖了,假如 100 个过程的话,就须要 400MB 的内存来存储页表,这是十分大的内存了,更别说 64 位的环境了。
为了解决空间上的问题,在对分页形式的根底上,进行优化,呈现了多级页表形式
多级页表
在后面咱们晓得了,分页形式在 32 位环境下,以每页 4KB 来计算,一共有 100 万页,「页表项」须要 4 个字节大小来存储,一个页表蕴含 100 万个「页表项」,那么每个过程的页表须要占用 4MB 大小,多级页表要如何解决这种问题呢?
在页表的根底上做一次二级分页,把 100 万「页表项」分为一级页表「1024 个页表项」,「一级页表项」下又关联二级页表「1024 个页表项」,这样一级页表的 1024 个页表项就笼罩到了 4GB 的空间范畴映射,并且二级页表按需加载,这样页表占用的空间就大大降低。
做个简略的计算,假如只有 20% 的一级页表项被用到了,那么页表占用的内存空间就只有 4KB(一级页表)+ 20% * 4MB(二级页表)= 0.804MB,这比照单级页表的 4MB 是不是一个微小的节约?
在这里插入图片形容
接着思考,在二级的根底上是不是又能够持续分级呢,能分二级,必然也能分三级、四级,在 64 位操作系统是做了四级分页,分为了四个目录,别离是
- 全局页目录项
- 下层页目录项
- 两头页目录项
- 页表项
在这里插入图片形容
TBL
多级页表尽管解决了空间上的问题,然而咱们发现这种形式须要走多道转换能力找到映射的物理内存地址,通过的多道转换造成了工夫上的开销。
程序是局部性的,即在一段时间内,整个程序的执行仅限于程序的某一部分。相应的,执行所拜访的存储空间也局限于某个内存区域。
操作系统就利用这一个性,把最多应用的几个页表项放到 TBL 缓存, CPU 在寻址时,会先查 TLB,如果没找到,才会持续查惯例的页表,TLB 的命中率其实很高的,因为程序最常拜访的页就那么几个。
内存段页
段式与页式并不是绝对的,他们也能够组合在一起应用,在段的根底上进行分页分级
- 先将程序划分为多个有逻辑意义的段,也就是后面提到的分段机制
- 接着再把每个段划分为多个页,也就是对分段划分进去的间断空间,再划分固定大小的页
虚拟地址构造由段号、段内页号和页内位移三局部组成
在这里插入图片形容
就是上面这几步
- 通过段号获取段表的段项
- 通过段项获取到页表地址
- 通过页表地址找到段页表
- 通过段内页号找到段页表的段页项
- 通过段页项获取物理页基地址
- 通过物理页基地址 + 偏移量计算出物理内存地址
-
- *
总结
过程并不是间接应用物理内存,而是通过虚构地址映射应用,所以操作系统会为每个过程调配虚拟空间(一套地址),使得每个过程应用物理内存互不影响,互相隔离。
启用大量过程造成内存缓和有余的时候,操作系统会通过内存替换技术,把不常应用的内存加载到硬盘(换出),应用时从硬盘加载到内存(换入)
操作系统对内存的治理形式分为三种,分段、分页、段页,分段的益处是物理内存空间是间断的,然而毛病很显著,容易造成内存碎片,并且内存替换效率慢,采纳分页能很好的解决分段的缺点,通过间断的虚拟地址解决了内部内存碎片问题,每次内存替换将最近不应用的内存以页的单位换出换入,保障替换数据大小,进步内存替换效率,然而会有页表空间占用问题,为了解决此问题,在分页的根底上优化成多级分页 +TBL 形式来缩小空间占用与工夫耗费,最初一个就是段页,段页是分段与分页的联合。
通过思考,咱们发现,多级分页通过树 + 懒加载 + 缓存解决了空间占用与工夫耗费的问题,虚拟地址很好的做到了让过程与物理内存地址解耦,正因如此,多过程应用物理内存时才不会有抵触,很好的做到了互相独立与隔离。
对于我
公众号 :「程序猿阿星」专一技术原理、源码,通过图解形式输入技术,这里将会分享操作系统、计算机网络、Java、分布式、数据库等精品原创文章,期待你的关注。