操作系统笔记(王道考研) 第三章:内存管理

大部分内容基于中国大学MOOC的2021考研数据结构课程所做的笔记,后续又根据2023年考研的大纲增加了一些内容,主要有操作系统引导、虚拟机、多级队列调度算法、互斥锁、调度器和闲逛进程、内存映射文件、文件系统的全局结构、虚拟文件系统、固态硬盘SSD、输入输出应用程序接口 、驱动程序接口等等。

感谢我的室友HXN,他帮我写了一部分第五章的内容。

课程内容和西电平时讲课的内容大致重合,西电可能每章会多讲一点UNIX系统的实例,可以听完这课再快速过一遍老师的课件防止漏掉什么内容。

这门课讲的其实不算特别硬核,没怎么涉及具体的代码。不过我其实感觉操作系统是个大无底洞,能学到多深基本取决于愿意花多少时间和精力。如果有闲心,推荐看下南大蒋炎岩老师的《操作系统:设计与实现》和哈工大李治军老师的《操作系统》,讲的更深入,当然难度也相应的大的多。

 

其他各章节的链接如下:

操作系统笔记(王道考研) 第一章:计算机系统概述

操作系统笔记(王道考研) 第二章:进程管理(1)

操作系统笔记(王道考研) 第二章:进程管理(2)

操作系统笔记(王道考研) 第三章:内存管理

操作系统笔记(王道考研) 第四章:文件管理

操作系统笔记(王道考研) 第五章:输入输出(I/O)管理

其他各科笔记汇总

内存管理

内存的基础知识

image-20210430163352830

什么是内存?有何作用?

image-20210430163239049

补充知识:几个常用的数量单位

image-20210430163535823

知识滚雪球:指令的工作原理

image-20210430163623814

此处的具体讲解略过不记,可以参考计算机组成网课第一章的内容

当程序运行的时候,系统会为它建立相应的进程。之前学过一个进程在内存当中会有一片区域叫作程序段用于存放进程相关的那些代码,指令。另外还有数据段用来存放程序所处理的一些变量之类的数据

image-20210430235202694

可执行文件又可称之为装入模块

形成可执行文件之后就可以把可执行文件放入内存里开始执行这个程序

该情况下程序装入模块是从内存地址为0这个地方开始往后依次存放的,若装入模块装入内存的时候从地址为100的这个地方开始,就意味着操作系统给这个进程分配的地址空间其实是100~279这么多,此时这个程序的逻辑地址和它最终存放的物理地址就会对应不上

image-20210501001314635

装入的三种方式 —— 绝对装入

image-20210501001710660

这种方式的灵活性很差,只适用于单道程序环境,早期还没有操作系统的阶段使用的就是这种方式

装入的三种方式 —— 可重定位装入

image-20210501001935074

这种方式的特点是我们给这个作业/进程分配的这些地址空间必须是连续的,并且这个作业必须一次全部装入内存

装入的三种方式 —— 动态运行时装入

image-20210501002502621

image-20210501002522221

CPU在对一个内存地址进行访问的时候会把逻辑地址和重定位寄存器当中存放的起始地址进行相加得到最终可以访问的地址

想让进程的数据在运行过程当中发生移动是很方便的,只需要修改重定位寄存器的值就可以

动态重定位的优点现在看不懂不要紧,后面学习虚拟存储管理之后会有更深入的理解

从写程序到程序运行

image-20210501003313740

这些目标模块文件当中已经包含了这些代码所对应的那些指令,而这些指令的编址都是逻辑地址。每一个模块的逻辑地址编址都是相互独立的,都从0开始

链接把这些目标模块组装起来形成一个完整的装入模块

在链接这一步,除了我们自己编写的这些目标模块需要链接以外,还需要把它们所调用到的一些库函数(如printf)也链接起来

链接的三种方式

image-20210501004538232

就是我们刚才所提到的这种方式,在形成装入模块之后就确定了这个装入模块的完整的逻辑地址

image-20210501004230387

采用这种方式的话,这个进程的完整的逻辑地址是一边装入一边形成的

image-20210501004252303

需要用到某一个模块的时候再把它调入内存,装入的同时进行链接

 

内存管理的概念

image-20210501120127542

内存空间的分配与回收

image-20210501120212583

进程想要投入运行,进程相关的数据需要放入内存当中

内存空间的扩展

image-20210501120556867

除了这个游戏之外,计算机当中也经常会遇到实际的内存空间不够所有的进程使用的问题,所以操作系统对内存进行管理也需要提供某一种技术实现所谓的虚拟性,把物理上很小的内存拓展为逻辑上很大的内存

地址转换

image-20210501121152348

image-20210501121114312

绝对装入是在编译的时候就产生了绝对地址,或者说在程序员写程序的时候直接就写了绝对地址。这个地址转换是由编译器而不是操作系统完成的

可重定位装入中地址转换的过程由装入程序负责进行,而装入程序也是操作系统的一部分

内存保护

image-20210501121926700

image-20210501122001503

内存保护让各个进程只能访问自己的内存空间而不能访问操作系统和别的进程的内存空间

image-20210501122022774

 

覆盖与交换

image-20210501122821617

覆盖技术

image-20210501122956029

image-20210501123014249

A这个模块会依次调用B模块和C模块。注意是依次调用,B模块和C模块只可能被A模块在不同的时间段调用,不可能是同时访问B和C这两个模块

操作系统并不知道程序的调用结构,程序的调用结构必须由程序员显性地声明,操作系统根据程序员声明的调用结构/覆盖结构完成自动覆盖

交换技术

image-20210501124022827

第二章讲处理机调度的时候讲过一个处理机调度层次的概念,分为高级调度,中级调度和低级调度。其中中级调度就是为了实现交换技术而使用的一种调度策略

换出外存的进程相关的PCB会保留在内存当中并且插入到挂起队列里

为什么进程的PCB要常驻内存呢?因为进程被换出外存之后,必须要通过某种方式记录下进程到底是放在外存的什么位置,这个信息记录在对应的PCB当中,操作系统就可以根据PCB当中记录的这些信息对这些进程进行管理

image-20210501124044889

一个本来处于就绪态的进程被换出外存,这个进程就处于就绪挂起态

一个本来处于阻塞态的进程被换出外存,这个进程就处于阻塞挂起态

image-20210501124113474

 

连续分配管理方式

image-20210501142451762

连续分配:指为用户进程分配的必须是一个连续的内存空间

单一连续分配

image-20210501142539960

内存当中同一时刻只能有一道用户程序,它并不支持多道程序并发运行

因为系统中只会运行一个用户程序,即使这个用户程序出问题了也只会影响用户程序本身,或者说即使这个用户程序越界损坏操作系统的数据,这个数据一般来说也可以通过重启计算机就可以很方便地进行修复,所以说采用单一连续分配的系统当中不一定采取内存保护

固定分区分配

image-20210501143120597

分区大小相等:如果有一个比较大的进程进入的话,如果这些分区的大小都不能满足这个大进程的需求,那么这个大进程就不能被装入这个系统,或者说只能采用覆盖技术在逻辑上来拓展各个分区的大小,而这显然又会增加一些系统开销

 

image-20210501143147510

动态分区分配

image-20210501143241230

此处对具体动态过程的讲解略过不记,应该不难理解

 

问题一

image-20210501150923980

 

问题二

image-20210501151115964

 

问题三

分区分配:

情况一

如果此时有一个进程5要申请4MB的内存空间,从20MB的空闲分区当中分配,对空闲分区表进行处理

image-20210501151844619

情况二

如果把4MB的空闲分区分配给进程5

image-20210501152037044

 

分区回收:

情况一

如果把进程4占用的空间回收

image-20210501152450534情况二

如果把进程3占用的空间回收

image-20210501152726516情况三

如果把进程4占用的空间回收

image-20210501152956281

情况四

如果把进程2占用的空间回收

image-20210501153142249

这几种情况本质上可以用一句话来进行总结:在进行内存分区回收的时候,如果说回收了之后发现有一些空闲分区是相邻的,那我们就需要把这些相邻的空闲分区全部合并

 

image-20210501153647728

紧凑技术其实就是把各个进程挪位,把它们全部攒到一起,挪出一个更大的连续空闲区间

动态重定位的方式最方便实现程序或者说进程在内存当中移动位置

紧凑之后需要修改各个进程的起始地址,进程的起始地址信息一般存放在进程对应的PCB当中,当进程要上CPU运行之前会把进程的起始地址信息放到重定位寄存器(基址寄存器)里

 

动态分区分配算法

image-20210501170817254

 

动态分区分配算法:在动态分区分配方式中, 当很多个空闲分区都能满足需求时,应该选择哪个分区进行分配?

前几种分配算法都比较简单,故会记得简略些

首次适应算法

算法思想:每次都从低地址开始查找,找到第一个能满足大小的空闲分区

如何实现:空闲分区以地址递增的次序排列。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区

最佳适应算法

算法思想:由于动态分区分配是一种连续分配方式,为各进程分配的空间必须是连续的一整片区域。因此为了保证当“大进程”到来时能有连续的大片空间,可以尽可能多地留下大片的空闲区,即优先使用更小的空闲区

如何实现:空闲分区按容量递增次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区

缺点:每次都选最小的分区进行分配,会留下越来越多的,很小的,难以利用的内存块。因此这种方法会产生很多的外部碎片

最坏适应算法

又称 最大适应算法(Largest Fit)

算法思想:为了解决最佳适应算法的问题——即留下太多难以利用的小碎片,可以在每次分配时优先使用最大的连续空闲区,这样分配后剩余的空闲区就不会太小,更方便使用

如何实现:空闲分区按容量递减次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区

缺点:每次都选最大的分区进行分配,虽然可以让分配后留下的空闲区更大,更可用,但是这种方式会导致较大的连续空闲区被迅速用完。如果之后有”大进程“到达,就没有内存分区可用了

邻近适应算法

算法思想:首次适应算法每次都从链头开始查找的。这可能会导致低地址部分出现很多小的空闲分区,而每次分配查找时,都要经过这些分区,因此也增加了查找的开销。如果每次都从上次查找结束的位置开始检索,就能解决上述问题

如何实现:空闲分区以地址递增的顺序排列(可排成一个循环链表)。每次分配内存时从上次查找结束的位置开始查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区

 

假如说此时系统当中的内存使用情况如下图所示,把这些空闲分区按照地址递增的次序依次进行排列,排成一个循环链表

如果有一个需要5MB内存空间的进程到达,就从链头的位置开始查找直到找到合适的分区进行分配。同时更新链当中的结点,包括分区的大小,还有分区的起始地址

image-20210501171024391

image-20210501171618361

采用邻近适应算法和首次适应算法只需要按照地址递增的次序进行排列。所以即使这里内存分区的大小发生了一个比较大的变化,但是我们依然不需要像最佳适应算法和最坏适应算法一样可能要对整个链表进行重新排列,算法的开销会比较小

接下来假如一个需要5MB内存空间的新进程到达,按照规则从上一次查找到的位置依次再往后查找即可。以后的过程都是类似的

image-20210501172201926

image-20210501172305706

 

首次适应算法每次都要从头查找,每次都需要检索低地址的小分区。但是这种规则也决定了当低地址部分有更小的分区可以满足需求时,会更有可能用到低地址部分的小分区,也会更有可能把高地址部分的大分区保留下来(最佳适应算法的优点)

邻近适应算法的规则可能会导致无论低地址、高地址部分的空闲分区都有相同的概率被使用,也就导致了高地址部分的大分区更可能被使用,划分为小分区,最后导致无大分区可用(最大适应算法的缺点)

综合来看,四种算法中,首次适应算法的效果反而更好

 

基本分页存储管理的概念

image-20210501173805635

 

非连续分配:为用户进程分配的可以是一些分散的内存空间

什么是分页存储

image-20210501173703830

内存里存放的无非就是各个进程的数据,包括进程的代码,进程的指令等等

重要的数据结构 —— 页表

image-20210501175229570

内存块就是页框(page flame)

image-20210501175214333

问题一:每个页表项占多少字节?

image-20210501175748594

这里计算机分配存储空间是以字节为单位而不是以bit为单位,所以20bit至少要用3B来存储

 

image-20210501180446292

image-20210501180536158

一个页表项在逻辑上是包含了页号和块号这两个信息,但是在物理上它只需要存放块号这个信息,只有块号需要占用存储空间

问题二:如何实现地址的转换

image-20210501181103207

image-20210501181204432

页内偏移量:相对于页面的起始位置而言的偏移量是多少

 

子问题:如何确定一个逻辑地址对应的页号,页内偏移量?

image-20210501181513633

image-20210501183134366

image-20210501183337426

image-20210501183429530

逻辑地址结构

image-20210501183808500

 

基本地址变换机构

image-20210501183941699

基本地址变换机构是基本分页存储管理中用于实现逻辑地址到物理地址转换的一组硬件机构

基本地址变换机构

在分页存储管理当中如果要把逻辑地址转换成物理地址的话,总共需要做4件事:

1.知道逻辑地址对应的页号

2.知道逻辑地址对应的页内偏移量

3.知道逻辑地址对应的页面在内存当中存放的位置到底是多少

4.根据页面在内存当中的起始位置和页内偏移量就可以得到最终的物理地址

image-20210501202410589

操作系统会把内存分为系统区和用户区。系统区当中存放着操作系统对整个计算机软硬件进行管理的一些相关的数据结构,包括PCB

如果一个进程被调度需要上处理机运行,进程切换相关的那些内核程序就会把这个进程的运行环境给恢复。这些进程运行环境相关的信息本来是保存在PCB当中的,内核程序会把这些信息放到相应的一系列寄存器当中,包括页表寄存器。另外程序计数器PC也是需要恢复的,PC指向这个进程下一条需要执行的指令的逻辑地址。接下来看怎么把这个逻辑地址转换成实际的物理地址,即CPU怎么在内存当中找到接下来要执行的这一条指令

image-20210501204037654

采用分页存储管理方式的系统当中,逻辑地址结构肯定是固定不变的,在一个逻辑地址当中页号有多少位,页内偏移量有多少位操作系统都是知道的。所以只要知道了逻辑地址就可以很快地切分出页号和页内偏移量这两个部分

接下来会对页号的合法性进行一个检查,越界就会抛出中断由中断处理程序进行处理

一个进程的页表长度M指的是这个进程的页表当中有M个页表项,也就意味着这个进程的页面总共有M页

如果页号是合法的,接下来会用这个页号和页表始址来进行计算,找到这个页号对应的页表项到底是多少

页表当中的每一个页表项的长度是相同的。所以只要知道了页号,页表起始地址和每一个页表项的长度就可以算出我们想要访问的页号对应的页表项所存放的位置

既然知道了存放的内存块号,就可以用内存块号结合页内偏移量得到最终的物理地址,然后就可以顺利地访问逻辑地址所对应的内存单元了

image-20210501205907921

 

该过程的文字描述如下

image-20210501205944359

1.计算页号P和页内偏移量W(如果用十进制数手算,则 P=A/L,W=A%L;但是在计算机实际运行时,逻辑地址结构是固定不变的,因此计算机硬件可以更快地得到二进制表示的页号,页内偏移量)

2.比较页号P和页表长度M,若 P ≥ M P\ge M PM,则产生越界中断,否则继续执行。(注意:页号从0开始的,而页表长度至少是1,因此 P=M 时也会越界)

3.页表中页号P对应的页表项地址 = 页表起始地址F + 页号P * 页表项长度,取出该页表项内容b,即为内存块号。(注意区分页表项长度,页表长度,页面大小的区别。页表长度指的是这个页表中总共有几个页表项,即总共有几个页;页表项长度指的是每个页表项占多大的存储空间;页面大小指的是一个页面占多大的存储空间)

image-20210501212150209

对页表项大小的进一步探讨

image-20210501212221246

页表项不能跨页框存储

如果说我们的这些页表项并不能装满整个页框的话,那在查找页表项的时候会造成一些麻烦

image-20210501212739749

具有快表的基本地址变换机构

image-20210501235504321

具有快表的基本地址变换机构是基本地址变换机构的改进版本

什么是快表

image-20210501224038585

接下来从操作系统的角度来看一下快表到底有什么作用

当进程上处理机运行时,系统会清空快表的内容。注意快表是一个专门的硬件,当进程切换的时候快表的内容也需要被清除

 

进程最先要访问的逻辑地址是(0,0)

首先进行越界异常的检查,该页号需要和页表寄存器当中的页表长度对比

接下来就会查询快表,由于快表此时的内容是空的,因此在快表中找不到页号为0对应的页表项,快表没有命中。接下来就不得不去访问内存当中存放的慢表,通过页表始址和页号计算出对应的页表项存放的位置

查询完慢表之后就可以知道0号页面所存放的内存块号是600,再通过内存块号和页内偏移量就可以得到最终的物理地址,最后就可以访问这个逻辑地址所对应的内存单元了

注意在访问了这个页表项之后同时也会把该页表项复制一份放到快表当中

 

接下来要访问的逻辑地址是(0,4)

同样地刚开始会进行越界异常的判断,发现没有越界

接下来会根据页号来查询快表以确认这个页号所对应的页表项是否在快表当中,由于刚才已经复制到了快表当中,因此这次的查询就可以命中。系统可以直接知道0号页面存放的页面是600,所以接下来就不需要查询内存当中的慢表而是直接用内存块号和页内偏移量得到最终想要访问的物理地址然后进行访存

image-20210501224207495

(0,0)前面指的是页号,后面指的是页内偏移量

引入快表后,地址的变换过程

image-20210501235613818

image-20210502001837057

局部性原理

image-20210501235653464

由于局部性原理,程序在某段时间内可能会频繁连续地访问某几个页面。而在地址变换的过程中,只要它访问的是同一个页面,那么它查询页表的时候查到的都是同一个页表项

 

两级页表

image-20210502002859194

单级页表存在的问题

image-20210502114135126

image-20210502114031976

只需要让进程此时会用到的那些页面对应的页表项在内存当中保存就可以了

如何解决单极页表的问题?

问题一:页表必须连续存放,因此当页表很大时,需要占用很多个连续的页框

问题二:没有必要让整个页表常驻内存,因为进程在一段时间内可能只需要访问某几个特定的页面

 

思考:我们是如何解决进程在内存中必须连续存储的问题的?

将进程地址空间分页,并为其建立一张页表,记录各页面的存放位置

同样的思路也可用于解决”页表必须连续存放“的问题,把必须连续存放的页表再分页

 

可将长长的页表进行分组,使每个内存块刚好可以放入一个分组(比如上个例子中,页面大小4KB,每个页表项4B,每个页面可存放1K个页表项,因此每1K个连续的页表项为一组,每组刚好占一个内存块,再将各组离散地放到各个内存块中)

另外,要为离散分配的页表再建立一张页表,称为页目录表,或称外层页表,或称顶层页表

两级页表的原理,地址结构

image-20210502142314028

注意以前在这个大页表当中编号为1024的这个页表项在进行拆分以后应该是变成了第2个小页表当中的第1个页表项,页号变为0

 

image-20210502142355732

为了记录这些小页表之间的相对顺序还有它们在内存当中存放的块号。需要为这些小页表再建立上一级的页表,这一级的页表就叫做页目录表。页目录表其实是建立了二级页表的页号还有二级页表在内存当中存放的块号之间的映射关系

采用了两级页表结构之后,逻辑地址的结构也要发生相应的变化

如何实现地址变换

image-20210502144305679

4.结合页内偏移量得到物理地址

如何解决单极页表的问题?

image-20210502144502714

缺页中断肯定是我们在执行某一条指令,这个指令想要访问到某一个暂时还没有调入的页面的时候产生的。所以这个中断信号和当前执行的指令有关,因此这种中断属于内中断

需要注意的几个细节

image-20210502144541717

如果没有快表机构的话,N 级页表在访问一个逻辑地址的时候访存次数应为 N+1次

 

基本分段存储管理

image-20210502145743776

 

与”分页“最大的区别就是 —— 离散分配时所分配地址空间的基本单位不同

分段

image-20210502145944576

由于是按逻辑功能模块划分,用户编程更方便,程序的可读性更高:

比如说用户可以用汇编语言写这样两条指令:

;<A><B>是用助记符表示的内存单元

LOAD 1,[D]|<A>;    ;将分段D中A单元的值读入寄存器1
STORE 1,[X]|<B>;   ;将寄存器1的内容存入X分段的B单元中

由于各个分段是按照逻辑功能来划分的,并且这些段名也是用户自己定义的,所以用户在读这个程序的时候就知道这两句代码做的事情就是把某个全局变量的值赋给X这个子函数当中的某个变量

在用户编程的时候使用段名来操作各个段,但是CPU具体执行的时候其实使用的是段号这个参数,所以编译程序会把这些段名转换成与它们各自相对应的段号,CPU在执行这些指令的时候根据段号来区分各个段

image-20210502152033118

段表

image-20210502152308284

地址变换

image-20210502152719360

 

下面看一下具体的变换过程

可以参考之前“基本地址变换机构”那一节中对地址变换过程的说明,基本类似。这里会记得简略一点

image-20210502154348361

段表存放的位置和段表长度这两个信息在进程没有上处理机运行的时候存放在进程的PCB当中。当进程上处理机运行的时候,这两个信息会被放到段表寄存器当中

image-20210502155046475

分段,分页管理的对比

image-20210502155444289

image-20210502160622843

image-20210502160551713

image-20210502160715928

 

段页式管理方式

image-20210502160804314

分页,分段的优缺点分析

image-20210502170514539

分页是按照信息的物理结构来进行划分的,分段是按照信息的逻辑结构来进行划分的

段式管理产生外部碎片的原理和动态分区分配很类似。对此的具体讲解此处略过不记,应该不难理解

对于外部碎片的解决其实也和之前介绍的动态分区分配一样,可以通过”紧凑“的方式来创造出更大的一片连续空间,但是需要付出较大的时间代价

分段+分页等于段页式管理

image-20210502171551745

每一个内存块的大小和系统当中的页面大小是一样的

段页式管理的逻辑地址结构

image-20210502171820297

此处的页号和页内偏移量其实就是分段管理当中的段内地址进行再拆分的结果

对于用户来说,他在编程的时候只需要关心段号和段内地址这两个信息,而剩下的分页是由操作系统完成的

段表,页表

image-20210502172218691

一个进程会对应一个段表,但是每个段会对应一个页表,故一个进程有可能会对应多个页表

 

系统当中也会有一个段表寄存器硬件,在这个进程上处理机运行之前会从PCB当中读出段表始址和段表长度这些信息然后放到段表寄存器当中

image-20210502172818477

 

虚拟内存的基本概念

image-20210502173848265

 

在传统存储管理方式的基础上引入了交换技术,覆盖技术,使得内存利用率有所提升,并且能从逻辑上扩充内存容量,这一小节要介绍的虚拟存储技术其实也是一种实现内存空间扩充的一种技术,它比交换技术和覆盖技术要更先进一些

传统存储管理方式的特征,缺点

image-20210502175015161

比如之前提到过的GTA,本来需要60G的内存,如果采用的是传统的存储管理方式,4GB的电脑是不可能运行60G大小的游戏的

玩GTA的时候,如果此时在游戏的A场景,那么B场景的资源就暂时不需要被加载到内存当中,我们只需要在内存当中放入A场景相关的资源就可以保证游戏的正常运行,而传统的存储管理方式做不到这点

局部性原理

之前讲过,也不难理解,这里就记得简略些了

image-20210502180454619

虚拟内存的定义和特征

image-20210502180556750

image-20210502180655446

多次性和对换性刚好就对应了传统的内存管理方案当中的一次性和驻留性

如何实现虚拟内存技术

image-20210502181232825

请求调页:在请求分页存储管理当中,如果我们所需要的页面暂时还不在内存当中,那么操作系统需要负责把这个页面从外存调入内存,并且完善一系列的处理

页面置换:当内存空间不够的时候,操作系统需要通过置换功能把暂时用不到的分页换出到外存

请求调页和页面置换对应的是请求分页存储管理方式,如果是请求分段存储管理方式的话,那么操作系统需要提供的就是请求调段和段置换功能

 

请求分页管理方式

image-20210502182017689

页表机制

image-20210502184439412

缺页中断机构

image-20210502190345108

中断处理的过程需要IO操作,把页面从外存调入内存。所以在等待IO操作完成的过程当中之前发生缺页的进程应该被阻塞放入到阻塞队列当中

 

如果内存中有空闲块:
假设a号块空闲

image-20210502185802006

image-20210502190208247

 

如果内存中没有空闲块:

假设页面选择算法选中了要淘汰2号页面,由于2号页面的内容是被修改过的,所以2号页面的内容需要从内存再写回外存,把外存当中的旧数据给覆盖掉,这样2号页面以前占有的C号块就可以空出来让0号页面使用了

image-20210502191432256

image-20210502190624894

image-20210502190801989

image-20210502192133128

由于它和当前执行的指令有关,因此缺页中断属于内中断

一条指令在执行的过程当中,有可能会产生多次缺页中断:因为一条指令当中可能会访问多个内存单元,而这些内存单元可能在不同的页面当中

地址变换机构

image-20210502192505189

新增步骤1:在查找到页面对应的页表项时,一定需要对页面是否在内存进行判断

新增步骤2:在地址变换的过程当中,如果我们发现此时想要访问的页面暂时没有调入内存,但是此时内存当中又没有空闲的内存块的时候,那么在这个地址变换的过程当中也需要进行页面置换的工作,换出某一些页面来腾出内存空间

新增步骤3:当页面调入或者调出或者页面被访问的时候需要对与它对应的页表项进行数据的修改

image-20210502193446409

页表始址M,页表长度N

 

上面并没有像讲基本分页管理方式时那样一步一步很仔细地分析,其实大家只需要关注请求分页管理方式与基本分页管理方式相比不同的那些步骤就可以了。下图的课本截图给出了很完整的请求分页管理方式当中地址变换的流程图

image-20210502195434301

一般来说在访问了某一个页面之后,只需要把这个页面在快表当中对应的表项的那些数据修改了就行了。只有它所对应的那些表项要在快表当中删除的时候才需要把这些数据从快表再复制回慢表当中

在中断处理的时候需要保护CPU的现场,然后让这个进程暂时进入阻塞态,只有这个进程重新回处理机运行的时候才需要恢复它的CPU现场

补充说明5:在具有快表机构的请求分页系统中,访问一个逻辑地址时,若发生缺页,则地址变换步骤是:

查快表(未命中)——查慢表(发现未调入内存)——调页(调入的页面对应的表项会直接加入快表)——查快表(命中)——访问目标内存单元

上图中右半部分的流程其实和基本分页存储管理方式进行地址变换的过程是大同小异的,只不过是增加了修改列表项相应的内容这一步骤。左半部分是新增的一系列处理,要多做的无非也就是请求调页,页面置换,修改页表数据结构等

 

页面置换算法

image-20210502200041026

 

用页面置换算法决定应该换出哪个页面

页面的换入,换出需要磁盘I/O,会有较大的开销,因此好的页面置换算法应该追求更少的缺页率,也就是让换入换出的次数尽可能地少

对OPT,FIFO,LRU算法执行过程的具体讲解略过不计,应该不难理解

最佳置换算法(OPT)

image-20210502221613887

刚开始为进程分配的内存块都是空的,要把要访问的页面放入内存块中

整个过程缺页中断发生了9次,页面置换发生了6次

注意:缺页时未必发生页面置换。若还有可用的空闲内存块,就不用进行页面置换

缺页率= 缺页中断发生的次数 总共访问了多少次页面 \frac{缺页中断发生的次数}{总共访问了多少次页面} 总共访问了多少次页面缺页中断发生的次数= 9/20 = 45%

image-20210502212527780

先进先出置换算法(FIFO)

image-20210502222054295

分配三个内存块时,缺页次数:9次

若分配四个内存块,则

image-20210502212900648

最近最久未使用置换算法(LRU)

image-20210502222416381

该算法的实现需要专门的硬件支持,虽然算法性能好,但是实现困难,开销大

时钟置换算法(CLOCK)

image-20210502214308430

当需要淘汰某一个页面的时候就扫描这个循环队列,找到一个最近没有被访问过的页面,也就是访问位为0的页面。扫描的过程中同时把扫描位为1的页面的访问位再重新置为0

 

下面举一个具体例子

若刚开始由于有5个空闲的内存块,所以前5个访问的页号都可以顺利地放入内存当中,只有在访问到6号页的时候才需要考虑淘汰某一个页面

image-20210502214708432

接下来从循环队列的队首开始扫描,尝试找到一个访问位为0的页面,并且被扫描过的页面需要把访问位1置为0

经过第一轮扫描之后,所有页面的访问位都由1置为0。在进行第二轮扫描的时候,1号页面的访问位为0,所以选择淘汰1号页面。于是6号页会装入到1号页以前占有的内存块当中,并且6号页的访问位会被置为1,然后扫描的指针指向下一个页面

image-20210502215343651

接下来依次访问3,4号页面,分别将其访问位由0置为1

image-20210502215534346

再之后要访问7号页,由于此时7号页没有在内存中,所以需要选择淘汰其中的某一个页面,然后把7号页放到内存中。同样地需要从此时扫描到的位置依次地扫描,找到第一个访问位为0的页面,并且扫描过的页面的访问位需要由1置为0。所以3和4号在扫描过后访问位会变为0

image-20210502220053503

扫描到2号页面时发现2号页面的访问位本来就是0,因此会选择淘汰2号页面,然后让7号页面放到这个位置,访问位置为1。然后扫描的指针指向下一个页面的位置

image-20210502220435762

改进型的时钟置换算法

这里对课上举的例子的具体执行讲解略过不记

image-20210502220746516

为了实现该算法,需要给各个页面增加一个修改位

 

页面分配策略,抖动,工作集

image-20210502223055332

页面分配,置换策略

image-20210502223150490

系统资源利用率之所以会降低,是因为系统当中的CPU还有I/O设备这两种资源理论上是可以并行地工作的,如果多道程序并发度下降,就意味着CPU和I/O设备这两种资源并行工作的几率就会小很多,资源的利用率当然就会有所降低

若驻留集太小,会导致缺页频繁,系统要花大量的时间来处理缺页,实际用于进程推进的时间很少;

驻留集太大,又会导致多道程序并发度下降,资源利用率降低。所以应该选择一个合适的驻留集大小。

固定分配:操作系统为每个进程分配一组固定数目的物理块,在进程运行期间不再改变。即,驻留集大小不变

 

image-20210502223310063

可变分配全局置换:刚开始会为每个进程分配一定数量的物理块。操作系统会保持一个空闲物理块队列。当某进程发生缺页时,从空闲物理块中取出一块分配给该进程;若已无空闲物理块,则可选择一个未锁定的页面换出外存,再将该物理块分配给缺页的进程。采用这种策略时,只要某进程发生缺页,都将获得新的物理块,仅当空闲物理块用完时,系统才选择一个未锁定的页面调出。被选择调出的页可能是系统中任何一个进程中的页,因此这个被选中的进程拥有的物理块会减少,缺页率会增加。

可变分配局部置换:刚开始会为每个进程分配一定数量的物理块。当某进程发生缺页时,只允许从该进程自己的物理块中选出一个进行换出外存。如果进程在运行中频繁地缺页,系统会为该进程多分配几个物理块,直至该进程缺页率趋势适当程度;反之,如果进程在运行中缺页率特别低,则可适当减少分配给该进程的物理块

何时调入页面

image-20210502235133822

实际应用当中,预调页策略和请求调页策略会结合着来使用。预调页用于进程运行前的调入,而请求调页策略在进程期间使用

何处调入页面

系统拥有足够的对换区空间:

image-20210503000036897

平时我们的程序在没有运行的时候相关的数据都是存放在文件区的

 

系统缺少足够的对换区空间:

image-20210503000054173

 

UNIX方式:

image-20210503000127015

抖动(颠簸)现象

image-20210503000500733

工作集

image-20210503000521486

在实际应用当中,窗口尺寸一般会设置的更大一些,比如说设置为10,50,100

对一些局部性很好的进程来说,工作集的大小一般是要比窗口尺寸的大小更小的

系统可以根据监测工作集的大小来决定到底要给这个进程分配多少个内存块,即根据工作集的大小确定驻留集的大小

内存映射文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jsHaJ606-1662139141792)(操作系统.assets/image-20220903010403436.png)]

传统的文件访问方式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P06ZpMSR-1662139141797)(操作系统.assets/image-20220903010604104.png)]

内存映射文件(Memory-Mapped Files)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ej5qkYXq-1662139141799)(操作系统.assets/image-20220903010508342.png)]
在这里插入图片描述

mmap系统调用会返回一个指针,指向刚才映射的这片内存区域的起始地址,接下来用这个指针就可以访问到文件中的任何数据

操作系统只是建立了文件数据和内存之间的映射关系,但并没有把文件数据直接读入内存,当访问数据时发现没有调入内存就会产生缺页异常,操作系统再自动把这块数据读入内存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o2YLslD2-1662139141802)(操作系统.assets/image-20220903010727747.png)]

两个进程的虚拟地址空间相互独立,但是操作系统会把这两块虚拟地址空间映射到相同的物理内存上

  • 9
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值