swap是什么意思,商务安排是什么意思

  

  入门操作系统是一门比较难的课程,操作系统的知识对于开发者来说非常重要。相信大家在学习操作系统的时候,有太多抽象的词汇和概念难以理解,可以直接劝阻。即使满血学习操作系统,不到3分钟睡意也会突然袭来。   

  

  所以我想把我的想法以图文并茂的白话形式,制作一系列操作系统的文章,让小白看得懂,帮助大家快速科普入门。   

  

  本文从内存的介绍开始,内存在操作系统中还是很重要的。了解它,就会对整个操作系统有一个初步的轮廓。   

  

  大纲   

  

  内容大纲   

  

  记忆的主要故事是什么?去地摊(准备跑程序流程)需要几个步骤?这里有一个猜测。   

  

  首先去城管申请摊位(申请记忆)。城管(操作系统)会根据剩余的地毯空间和你地毯的大小给你分一个相应大小的摊位(内存)。然后你就可以开开心心的摆摊(跑程序流程)赚钱了。   

  

  城管还会不定期检查(整理记忆空间碎片)摊位是否正规,是否妨碍正常人行道。   

  

  简而言之,计算机上的程序(进程)需要使用相应大小的物理内存来运行。   

  

  实际上,虚拟内存运行的进程并不直接使用物理内存地址,而是将进程使用的内存地址与实际物理内存地址隔离开来,即操作系统会为每个进程分配一个独立的“虚拟地址”集合。   

  

  每个进程都有自己的地址,互不干扰。至于虚拟地址如何映射到物理地址,对进程是透明的,操作系统已经把这些安排说清楚了。   

  

  操作系统将提供一种机制,将不同进程的虚拟地址映射到不同内存的物理地址,如下图所示。   

  

     

  

  这就引出了两个概念:   

  

  进程使用的内存地址称为虚拟地址,计算硬件中的空间地址称为物理地址.简单来说,操作系统引入虚拟空间,进程持有的虚拟地址会通过CPU芯片中内存管理单元(MMU)的映射关系转换成物理地址,然后通过物理地址访问物理内存。   

  

  操作系统如何管理虚拟地址和物理内存地址之间的关系?主要有三种方式,即分段、分页、段页.让我们来看看这三种内存管理方式。   

  

  内存分段程序包含几个逻辑段,如代码段、数据段、堆栈段和堆段。每个段都有不同的属性,所以内存把这些段以段的形式分离出来进行管理。   

  

  在内存分段模式下,虚拟地址是如何映射到物理地址的?分段管理下的虚拟地址由段号和段内偏移量两部分组成。   

  

     

  

  在此插入图片描述。   

  

  通过将段号映射到段基址偏移量=使用的物理内存,从段表的条目中获取段基址。从上面我们知道,段号是用来映射段表中的条目的,物理内存地址是利用条目中的段基址和偏移量来计算的。但实际上,分段法是将程序的虚拟地址分成四段,每段在段表中都有一个条目。在这个条目中找到段基址,加上偏移量计算物理内存地址。   

  

  分段方式,一个很好的解决方案,程序本身不需要关心具体的物理内存地址,但它还是有一些缺点:   

  

  内存碎片问题,内存交换效率低。接下来分析这两个问题。   

  

  分段是如何导致内存碎片的?在说内存碎片之前,我们先来了解一下什么是内存碎片。8个人出去吃饭,因为饭点,人比较多,剩下的都是4个人的小桌子。这些4个人的小桌子就是我们所说的记忆碎片。这时候有朋友会说,把两张4人用的小桌子拼起来就解决这个问题了,很简单。我们称这种方法为内存碎片整理(涉及内存交换)。   

  

  回到正题,我们来看一个例子。假设物理内存只有1GB (1024MB ),用户的计算机上运行着多个程序:   

  

  浏览器占128MB,音乐软件占256MB,游戏占512MB。此时我们关闭浏览器,剩余物理内存为1024MB -(256MB 512MB)=256MB。但剩余的256MB物理内存并不连续,被分成两段128MB,导致没有空间再打开一个200MB的程序,如下图所示。   

  

     

  

  在此插入图片描述。   

  

  这里有两个内存碎片问题:   

  

  外存碎片是若干个不连续的小物理内存空间,导致新程序无法加载内存碎片。程序的内存全部加载到物理内存中,但是部分程序有内存,可能不会经常使用,造成内存浪费。解决外部内存碎片的方法是使用内存碎片整理。   

  

  通过内存进行内存碎片整理   

交换的方式来实现,我们可以把音乐软件占用的256MB加载到硬盘上面去,再从硬盘读取回来,但是读取回来的位置不再是原来的位置,而是紧跟已经占用的游戏512MB后面,这样两个128MB的空闲物理内存就合并成了一个256MB的连续物理内存,于是新的200MB新程序就能被装载进来

  

内存交换空间,在 Linux 系统里,是我们常看到的 Swap 空间,这块空间是从硬盘划分出来的,用于内存与硬盘的空间交换。

  

分段方式为什么内存交换效率低?首先分段管理容易造成内存碎片,导致内存交换的频率较高,因为硬盘的访问速度比内存慢太多了,然后每次交换的时候,把一大段连续的内存写入到硬盘,再又从硬盘读取出来,如果交换的是一个占内存空间很大的程序,这样整个机器都会显得卡顿,过程也很慢的,所以说分段方式内存交换效率低。

  

为了解决内存分段管理造成的内存碎片与内存交换效率低的问题,就出现了内存分页

  

内存分页分段的好处是能产生连续的内存空间,但是会出现大量内存碎片与内存交换效率低的问题

  

先思考一下怎么解决这两个问题,内存碎片是由多个不连续的小物理内存空间造成,如果把这些不连续的小物理内存空间组合起来,是不是解决了这个问题?同样的,内存交换的时候我们保证交换的数据小,是不是能提高内存交换的效率?

  

这个办法就是内存分页,分页是把整个虚拟与物理空间切成一段段固定尺寸的大小,这样一个连续并且尺寸固定的空间,我们叫页,在 Linux 下,每一页的大小为 4KB。(虚拟空间是指存储一套虚拟地址的空间)

  

虚拟地址与物理地址是通过页表来映射,虚拟空间内的虚拟地址一定是连续的,物理地址不一定,但可以通过连续的虚拟地址把多个不连续的物理内存组合使用。

  

  

在这里插入图片描述

  

而当进程访问的虚拟地址在页表中查不到时,系统会产生一个缺页异常,进入系统内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行。

  

分页方式是如何解决内存碎片与内存交换效率慢的问题呢?内存碎片的解决:

  

因为使用内存的单位变成固定大小的页,所以每个程序的虚拟空间维护的也是连续的页(虚拟地址),通过页表再映射到物理内存页,虽然映射的物理内存页不连续,但是虚拟空间是连续的,可以让它们组合起来使用,但这也只能解决外部内存碎片问题,没有解决内部内碎片问题,因为每页都有固定大小,可能某一页只使用了部分,依然会造成一些浪费。

  

内存交换效率慢的解决:

  

之前说过,减少交换数据的大小,可以提高内存交换效率,分页方式是这样解决的,如果内存空间不够时,操作系统会把其他正在运行的进程中的「最近没被使用」的内存页释放掉,也就是加载到硬盘,称为换出,一旦需要的时候再加载进来,称为换入。所以一次性写入硬盘的也只有一个页或几个页,内存的交换效率自然就提升了。

  

分页方式使加载程序的时候,不再需要一次性都把程序加载到物理内存中。完全可以在进行虚拟内存和物理内存的页之间的映射之后,并不真的把页加载到物理内存里,而是只有在程序运行中,需要用到对应虚拟内存页里面的指令和数据时,再加载到物理内存里面去(用大白话说,当你需要用到的时候才会去使用对应的物理内存)。

  

在内存分页方式下,虚拟地址和物理地址是如何映射的?在分页机制下,每个进程都会分配一个页表,虚拟地址会分为两部分,页号和页内偏移量,页号作为页表的索引,页表包含物理页每页所在物理内存的基地址,页内偏移量+物理内存基地址就组成了物理内存地址,如下图所示

  

  

在这里插入图片描述

  

就是下面这几步

  

页号找到页表中的页项获取页项的物理页号基地址偏移量+物理页号基地址计算出物理内存地址是不是非常的简单,但是这种分页方式使用到操作系统上会不会问题呢?那必然是会有问题的,还记得之前提到的每个进程会分配一个页表嘛?下面来为大家解开这个伏笔

  

在分页方式下,每个进程分配一个页表会有什么问题?不卖关子了,每个进程分配一个页表会有空间上的缺陷,因为操作系统上可以运行非常多的进程,那不就意味着页表数量非常多!

  

1B(Byte 字节)=8bit, 1KB (Kilobyte 千字节)=1024B, 1MB (Megabyte 兆字节 简称“兆”)=1024KB, 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方式来减少空间占用与时间消耗,最后一个就是段页,段页是分段与分页的结合。

  

通过思考,我们发现,多级分页通过树+懒加载+缓存解决了空间占用与时间消耗的问题,虚拟地址很好的做到了让进程与物理内存地址解耦,正因如此,多进程使用物理内存时才不会有冲突,很好的做到了相互独立与隔离。

相关文章