OS重要知识点摘录(考研用)——第三章:内存管理
本文参考于《2021年操作系统考研复习指导》(王道考研),《计算机操作系统教程》
1.程序装入和链接
创建进程首先要将程序和数据装入内存。将用户源程序变为可在内存中执行的程序,通常需要以下几个步骤:
(1)编译
由编译程序将用户源代码编译成若干目标模块
(2)链接
由链接程序将编译后形成的一组目标模块及所需的库函数链接在一起,形成一个完整的装入模块
(3)装入
由装入程序将装入模块装入内存运行
程序的链接有以下三种方式:
(1)静态链接
在程序运行之前,先将各目标模块及它们所需的库函数链接成一个完整的可执行程序,以后不再拆开
(2)装入时动态链接
将用户源程序编译后所得到的一组目标模块,在装入内存时,采用边装入边链接的方式
(3)运行时动态链接
对某些目标模块的链接,是在程序执行中需要该目标模块时才进行的。其优点是便于修改和更新,便于实现对目标模块的共享
内存的装入模块在装入内存时,同样有以下三种方式:
(1)绝对装入
在编译时,若知道程序将驻留在内存的某个位置,则编译程序将产生绝对地址的目标代码。绝对装入程序按照装入模块中的地址,将程序和数据装入内存。由于程序中的逻辑地址与实际内存地址完全相同,因此不需对程序和数据的地址进行修改。 绝对装入方式只适用于单道程序环境。 另外,程序中所用的绝对地址,可在编译或汇编时给出,也可由程序员直接赋予。而通常情况下在程序中采用的是符号地址,编译或汇编时再转换为绝对地址。
(2)可重定位装入
在多道程序环境下,多个目标模块的起始地址(简称始址)通常都从0开始,程序中的其他地址都是相对于始址的,此时应采用可重定位装入方式。根据内存的当前情况,将装入模块装入内存的适当位置。装入时对目标程序中指令和数据的修改过程称为重定位,地址变换通常是在装入时一次完成的,所以又称静态重定位。
静态重定位的特点是:一个作业装入内存时,必须给它分配要求的全部内存空间,若没有足够的内存,则不能装入该作业。此外,作业一旦进入内存,整个运行期间就不能在内存中移动,也不能再申请内存空间。
(3)动态运行时装入,也称动态重定位
程序在内存中若发生移动,则需要采用动态的装入方式。装入程序把装入模块装入内存后,并不立即把装入模块中的相对地址转换为绝对地址,而是把这种地址转换推迟到程序真正要执行时才进行。 因此,装入内存后的所有地址均为相对地址。这种方式需要一个重定位寄存器的支持。
动态重定位的特点如下:可以将程序分配到不连续的存储区中;在程序运行之前可以只装入它的部分代码即可投入运行,然后在程序运行期间,根据需要动态申请分配内存;便于程序段的共享,可以向用户提供一个比存储空间大得多的地址空间。
2.逻辑地址空间与物理地址空间
编译后,每个目标模块都从0号单元开始编址,这称为该目标模块的相对地址(或逻辑地址)。 当链接程序将各个模块链接成一个完整的可执行目标程序时,链接程序顺序依次按各个模块的相对地址构成统一的从0号单元开始编址的逻辑地址空间。用户程序和程序员只需知道逻辑地址,而内存管理的具体机制则是完全透明的,只有系统编程人员才会涉及内存管理的具体机制。不同的进程可以有相同的逻辑地址,因为这些相同的逻辑地址可以映射到主存的不同位置。
物理地址空间是指内存中物理单元的集合,它是地址转换的最终地址, 进程在运行时执行指令和访问数据,最后都要通过物理地址从主存中存取。当装入程序将可执行代码装入内存时,必须通过地址转换将逻辑地址转换成物理地址,这个过程称为地址重定位。
3.内存保护
内存分配前,需要保护OS不受用户进程的影响,同时保护用户进程不受其他用户进程的影响。内存保护可采取两种方法:
(1)在CPU中设置一对上、下限寄存器,存放用户作业在主存中的下限和上限地址,每当CPU要访问一个地址时,分别和两个寄存器的值相比,判断有无越界
(2)采用重定位寄存器(或基址寄存器)和界地址寄存器(又称限长寄存器) 来实现这种保护。重定位寄存器含最小的物理地址值,界地址寄存器含逻辑地址的最大值。 每个逻辑地址值必须小于界地址寄存器;内存管理机构动态地将逻辑地址与界地址寄存器进行比较,若未发生地址越界,则加上重定位寄存器的值后映射成物理地址,再送交内存单元
当CPU调度程序选择进程执行时,派遣程序会初始化重定位寄存器和界地址寄存器。每个逻辑地址都需要与这两个寄存器进行核对,以保证OS和其他用户程序及数据不被该进程的运行影响。
重定位寄存器是用来“加”的,逻辑地址加上重定位寄存器中的值就能得到物理地址;界地址寄存器是用来“比”的,通过比较界地址寄存器中的值与逻辑地址的值来判断是否越界
3.1.3 连续分配管理方式
连续分配方式是指为一个用户程序分配一个连续的内存空间, 譬如某用户需要1GB的内存空间,连续分配方式就在内存空间中为用户分配一块连续的1GB空间。连续分配方式主要包括单一连续分配、固定分区分配和动态分区分配
1.单一连续分配
内存在此方式下分为系统区和用户区,系统区仅供OS使用,通常在低地址部分;用户区是为用户提供的、除系统区之外的内存空间。 这种方式无须进行内存保护。因为内存中永远只有一道程序,因此肯定不会因为访问越界而干扰其他程序。
这种方式的优点是简单、无外部碎片,可以采用覆盖技术,不需要额外的技术支持。缺点是只能用于单用户、单任务的OS中, 有内部碎片,存储器的利用率极低
2.固定分区分配
固定分区分配是最简单的一种多道程序存储管理方式,它将用户内存空间划分为若干固定大小的区域,每个分区只装入一道作业。当有空闲分区时,便可再从外存的后备作业队列中选择适当大小的作业装入该分区,如此循环。
固定分区分配在划分分区时有两种不同的方法:
(1)分区大小相等:用于利用一台计算机去控制多个相同对象的场合,缺乏灵活性
(2)分区大小不等:划分为多个较小的分区、适量的中等分区和少量大分区
为便于内存分配,通常将分区按大小排队,并为之建立一张分区说明表,其中各表项包括每个分区的始址、大小及状态(是否已分配),如图所示:
当有用户程序要装入时,便检索该表,以找到合适的分区给予分配并将其状态置为已分配;未找到合适分区时,则拒绝为该用户程序分配内存。存储空间的分配情况如图:
这种分区方式存在两个问题:一是程序可能太大而放不进任何一个分区中,这时用户程序不得不使用覆盖技术来使用内存空间;二是主存利用率低,当程序小于固定分区大小时,也占用一个完整的内存分区空间,这样分区内部就存在空间浪费,这种现象称为内部碎片。
固定分区是可用于多道程序设计的最简单的存储分配,无外部碎片,但不能实现多进程共享一个主存区,所以存储空间利用率低。固定分区分配很少用于现在通用的OS中,但在某些用于控制多个相同对象的控制系统中仍发挥着一定的作用。
3.动态分区分配
动态分区分配又称可变分区分配,是一种动态划分内存的分区方法。这种分区方法不预先划分内存,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。 因此,系统中分区的大小和数目是可变的
如图所示,系统有64MB内存空间,其中低8MB固定分配给OS,其余为用户可用内存。开始时装入前三个进程,它们分别分配到所需的空间后,内存只剩下4MB,进程4无法装入。在某个时刻,内存中没有一个就绪进程,CPU出现空闲,OS就换出进程2,换入进程4。由于进程4比进程2小,这样在主存中就产生了一个6MB的内存块。之后CPU又出现空闲,而主存无法容纳进程2,OS就换出进程1,换入进程2。
动态分区在开始分配时是很好的,但之后会导致内存中出现许多小的内存块。随着时间的推移,内存中会出现越来越多的碎片,内存的利用率随之下降。 这些小的内存块称为外部碎片, 指在所有分区外的存储空间会变成越来越多的碎片,这与固定分区中的内部碎片正好相对。克服外部碎片可以通过紧凑(Compaction)技术来解决,即OS不时地对进程进行移动和整理。 但这需要动态重定位寄存器的支持,且相对费时。紧凑的过程实际上类似于Windows中的磁盘整理程序,只不过后者是对外存空间的紧凑。
在进程装入或换入主存时,若内存中有多个足够大的空闲块,则OS必须确定分配哪个内存块给进程使用,这就是动态分区的分配策略。 考虑以下几种算法:
(1)首次适应(First Fit)算法
空闲分区以地址递增的次序链接。分配内存时顺序查找,找到大小能满足要求的第一个空闲分区。
(2)最佳适应(Best Fit)算法
空闲分区按容量递增的方式形成分区链, 找到第一个能满足要求的空闲分区
(3)最坏适应(Worst Fit)算法
又称最大适应算法,空闲分区以容量递减的次序链接,找到第一个能满足要求的空闲分区,即挑选出最大的分区
(4)邻近适应(Next Fit)算法
又称循环首次适应算法,由首次适应算法演变而成。不同之处是,分配内存时从上次查找结束的位置开始继续查找。
在这几种方法中,首次适应算法不仅是最简单的,而且通常也是最好的和最快的。 在UNIX系统的最初版本中,就是使用首次适应算法为进程分配内存空间的,它使用数组的数据结构(而非链表)来实现。不过,首次适应算法会使得内存的低地址部分出现很多小的空闲分区,而每次分配查找时,都要经过这些分区,因此增加了查找的开销。
邻近适应算法试图解决上述问题。但实际上,它常常导致在内存的末尾分配空间(因为在一遍扫描中,内存前面部分使用后再释放时,不会参与分配),使位于存储空间末尾的大分区被撕裂成小的外部碎片。它通常比首次适应算法的结果要差
最佳适应算法的性能通常很差,因为每次最佳的分配会留下很小的难以利用的内存块,会产生最多的外部碎片。
3.1.4 非连续分配管理方式
非连续分配允许一个程序分散地装入不相邻的内存分区。 在连续分配管理方式中,即使内存有超过1GB的空闲空间,但若没有连续的1GB空间,则需要1GB空间的作业仍然是无法运行的;但若采用非连续分配管理方式,则作业所要求的1GB内存空间可以分散地分配在内存的各个区域,当然,这也需要额外的空间去存储它们(分散区域)的索引,使得非连续分配方式的存储密度低于连续存储方式的。
非连续分配管理方式根据分区的大小是否固定,分为分页存储管理方式和分段存储管理方式。
在分页存储管理方式中,又根据运行作业时是否要把作业的所有页面都装入内存才能运行,分为基本分页存储管理方式和请求分页存储管理方式。
1.基本分页存储管理方式
固定分区会产生内部碎片,动态分区会产生外部碎片, 这两种技术对内存的利用率都比较低。我们希望内存的使用能尽量避免碎片的产生,这就引入了分页的思想:把主存空间划分为大小相等且固定的块,块相对较小,作为主存的基本单位。每个进程也以块为单位进行划分,进程在执行时,以块为单位逐个申请主存中的块空间。
分页的方法从形式上看,有点像分区相等的固定分区技术,分页管理不会产生外部碎片。 但它又有着本质的不同点:块的大小相对分区要小很多,而且进程也按照块进行划分,进程运行时按块申请主存可用空间并执行。这样,进程只会在为最后一个不完整的块申请一个主存块空间时,才产生主存碎片, 所以尽管会产生内部碎片,但这种碎片相对于进程来说也是很小的,每个进程平均只产生半个块大小的内部碎片(也称页内碎片)
(1)分页存储的几个基本概念
a.页面和页面大小
进程中的块称为页(Page),内存中的块称为页框(Page Frame,或页帧)。 外存也以同样的单位进行划分,直接称为块(Block)。进程在执行时需要申请主存空间,即要为每个页面分配主存中的可用页框,这就产生了页和页框的一 一对应。
为方便地址转换,页面大小应是2的整数幂。同时页面大小应该适中,页面太小会使进程的页面数过多,这样页表就会过长,占用大量内存,而且也会增加硬件地址转换的开销,降低页面换入/换出的效率;页面过大又会使页内碎片增多,降低内存的利用率。
b.地址结构
分页存储管理的逻辑地址结构如图:
地址结构包含两部分:前一部分为页号P,后一部分为页内偏移量W。 地址长度32位,其中0~11位为页内地址,即每页大小为4KB;12 ~ 31位为页号,地址空间最多允许2^20页
地址结构决定了虚拟内存的寻址空间有多大
c.页表
为了便于在内存中找到进程的每个页面所对应的物理块,系统为每个进程建立一张页表,它记录页面在内存中对应的物理块号,页表一般存放在内存中。
页表是由页表项组成的。 注意页表项与地址的区别,页表项与地址都由两部分构成,而且第一部分都是页号,但页表项的第二部分是物理内存中的块号, 而地址的第二部分是页内偏移;页表项的第二部分与地址的第二部分共同组成物理地址。
在配置页表后,进程执行时,通过查找该表,即可找到每页在内存中的物理块号。页表的作用是实现从页号到物理块号的地址映射。
(2)基本地址变换机构
地址变换机构的任务是将逻辑地址转换为内存中的物理地址。地址变换是借助于页表实现的。 下图为分页存储管理系统中的地址变换机构
在系统中通常设置一个页表寄存器(PTR),存放页表在内存的起始地址F和页表长度M。进程未执行时,页表的始址和长度存放在进程控制块中,当进程执行时,才将页表始址和长度存入页表寄存器。 设页面大小为L,逻辑地址A到物理地址E的变换过程如下(逻辑地址、页号、每页的长度都是10进制数):
a.计算页号P(P=A/L)和页内偏移量W(W=A%L)
b.比较页号P和页表长度M,若P>=M,则产生越界中断,否则继续执行
c.页表中页号P对应的页表项地址=页表始址F+页号P x 页表项长度,取出该页表项内容b,即为物理块号。注意页表长度和页表项长度的区别,页表长度的值是指一共有多少页,页表项长度是指页地址占多大的存储空间。
d.计算E=b x L+W,用得到的物理地址E去访问内存
以上整个地址变换过程都是由硬件自动完成的。 例如,若页面大小L为1KB,页号2对应的物理块为b=8,计算逻辑地址A=2500的物理地址E:P=2500/1K=2,W=2500%1K=452,查找得到页号2对应的物理块号为8,E=8 x 1024+452=8644
页式管理只需给出一个整数就能确定对应的物理地址,因为页面大小L是固定的。因此,页式管理中地址空间是一维的。
页表项的作用是找到该页在内存中的位置。以32位逻辑地址空间、字节编址单位、一页4KB为例,地址空间内一共有2^32B/4KB=1M页,因此需要20位才能保证表示范围能容纳所有页面,又因为以字节作为编址单位,即页表项的大小>=3B。为保证页表项能指向所有页面,页表项的大小应该>=3B,也可选择更大的页表项让一个页面能够正好容下整数个页表项,进而方便存储(如取成4B,一页正好可以装下1K个页表项),或增加一些其他信息。
下面讨论分页管理方式存在的两个主要问题:a.每次访存操作都需要进行逻辑地址到物理地址的转换,地址转换过程必须足够快,否则访存速度会降低;b.每个进程引入页表,用于存储映射机制,页表不能太大,否则内存利用率会降低
(3)具有快表的地址变换机构
若页表全部放在内存中,则存取一个数据或一条指令至少要访问两次内存:第一次是访问页表,确定所存取的数据或指令的物理地址;第二次是根据该地址存取数据或指令。
在地址变换机构中增设一个具有并行查找能力的高速缓冲存储器——快表,又称相联存储器(TLB),用来存放当前访问的若干页表项,以加速地址变换的过程。 与此对应,主存中的页表常称为慢表。
在具有快表的分页机制中,地址的变换过程如下:
a.CPU给出逻辑地址后,由硬件进行地址转换,将页号送入高速缓存寄存器,并将此页号与快表中的所有页号进行比较
b.若找到匹配的页号,说明所要访问的页表项在快表中,则直接从中取出该页对应的页框号,与页内偏移量拼接形成物理地址。这样,存取数据仅一次访存便可实现。
c.若未找到匹配的页号,则需要访问主存中的页表,在读出页表项后,应同时将其存入快表, 以便后面可能的再次访问。但若快表已满,则必须按照一定的算法对旧的页表项进行替换。
注意:有些处理机设计为快表和慢表同时查找,若在快表中查找成功则终止慢表的查找
快表的有效性基于程序的局部性原理
(4)两级页表
由于引入了分页管理,进程在执行时不需要将所有页调入内存页框,而只需将保存有映射关系的页表调入内存。但我们仍需考虑页表的大小。
以32位逻辑地址空间、页面大小4KB、页表项大小为4B,一个40MB的进程为例,页表项共40KB,若将所有页表项内容保存在内存中,则需要10个内存页框来保存整个页表。为了压缩页表,应用时间换空间的思想,引入二级分页,即使用层次结构的页表:将页表的10页空间也进行地址映射,建立上一级页表,用于存储页表的映射关系。一级页表的页表项是二级页表的起始地址,这里对页表的10个页面进行映射只需要10个页表项,所以上一级页表只需要1页就已足够。在进程执行时,只需要将这一页的上一级页表调入内存即可,进程的页表和进程本身的页面可在后面的执行中再调入内存。
需要一张索引表来告诉我们第几张页表该上哪里去找,这能解决页表的查询问题,且不用把所有的页表都调入内存,只在需要它时才调入,因此能解决占用内存空间过大的问题。实际上就是构造一个页表的页表,也就是二级页表。为查询方便,顶级页表最多只能有一个页面。 因此顶级页表总共可以容纳4KB/4B=1K个页表项,它占用的地址位数为10位,而页内偏移地址占12位,因此一个32位的逻辑地址空间就剩下了10位,正好使得二级页表的大小在一页之内,这样就得到了逻辑地址空间的格式
二级页表实际上是在原有页表结构上再加上一层页表
建立多级页表的目的在于建立索引,以便不用浪费主存空间去存储无用的页表项,也不用盲目地顺序式查找页表项
多级页表解决了当逻辑地址空间过大时,页表的长度会大大增加的问题。而采用多级页表时,一次访问过程需要多次访问内存甚至磁盘,会大大增加一次访存的时间
2.基本分段存储管理方式
分页管理方式是从计算机的角度考虑设计的,目的是提高内存的利用率,提升计算机的性能。分页通过硬件机制实现,对用户完全透明。分段管理方式的提出则考虑了用户和程序员,以满足方便编程、信息保护和共享、动态增长及动态链接等多方面的需要。
(1)分段
段式管理方式按照用户进程中的自然段划分逻辑空间。 例如,用户程序由主程序、两个子程序、栈和一段数据组成,于是可以把这个用户进程划分为5段,每段从0开始编址,并分配一段连续的地址空间(段内要求连续,段间不要求连续,因此整个作业的地址空间是二维的),其逻辑地址由段号S与段内偏移量W两部分组成。
在下图中,段号为16位,段内偏移量为16位,因此一个作业最多有2^16=65536段,最大段长为64KB。
在页式系统中,逻辑地址的页号和页内偏移量对用户是透明的,但在段式系统中,段号和段内偏移量必须由用户显式提供,在高级程序设计语言中,这个工作由编译程序完成。
(2)段表
每个进程都有一张逻辑空间与内存空间映射的段表,其中每个段表项对应进程的一段,段表项记录该段在内存中的始址和长度。
配置段表后,执行中的进程可通过查找段表,找到每段所对应的内存区。段表用于实现从逻辑段到物理内存区的映射
(3)地址变换机构
在系统中设置了段表寄存器,用于存放段表始址F和段表长度M。从逻辑地址A到物理地址E的地址变换过程如下:
a.从逻辑地址A中取出前几位为段号S,后几位为段内偏移量W
b.比较段号S和段表长度M,若S>=M,则产生越界中断,否则继续执行
c.段表中段号S对应的段表项地址=段表始址F+段号S x 段表项长度,取出该段表项的前几位得到段长C。若段内偏移量>=C,则产生越界中断,否则继续执行。段表项实际上只有两部分,前几位是段长,后几位是始址。
d.取出段表项中该段的始址b,计算E=b+W,用得到的物理地址E去访问内存。
(4)段的共享与保护
在分段系统中,段的共享是通过两个作业的段表中相应表项指向被共享的段的同一个物理副本来实现的。 当一个作业正从共享段中读取数据时,必须防止另一个作业修改此共享段中的数据。不能修改的代码称为纯代码或可重入代码(它不属于临界资源),这样的代码和不能修改的数据可以共享,而可修改的代码和数据不能共享。
与分页管理类似,分段管理的保护方法主要有两种:一种是存取控制保护,另一种是地址越界保护。地址越界保护将段表寄存器中的段表长度与逻辑地址中的段号比较,若段号大于段表长度,则产生越界中断;再将段表项中的段长与逻辑地址中的段内偏移进行比较,若段内偏移大于段长,也会产生越界中断。 分页管理中的地址越界保护只需要判断页号是否越界,页内偏移是不可能越界的。
与页式管理不同,段式管理不能通过给出一个整数便确定对应的物理地址,因为每段的长度是不固定的,无法通过整数除法得出段号,无法通过求余得出段内偏移,所以段号和段内偏移一定要显式给出 (段号,段内偏移),因此分段管理的地址空间是二维的
分页管理方式和分段管理方式各方面的对比:
3.段页式管理方式
页式存储管理能有效地提高内存利用率,而分段存储管理能反映程序的逻辑结构并有利于段的共享。将这两种存储管理方法结合起来,便形成了段页式存储管理方式。
在段页式系统中,作业的地址空间首先被分成若干逻辑段,每段都有自己的段号,然后将每段分成若干大小固定的页。对内存空间的管理仍然和分页存储管理一样,将其分成若干和页面大小相同的存储块,对内存的分配以存储块为单位。
在段页式系统中,作业的逻辑地址分为三部分:段号、页号和页内偏移量
为了实现地址变换,系统为每个进程建立一张段表,每个分段有一张页表。 段表表项中至少包括段号、页表长度和页表始址,页表表项中至少包括页号和块号。 此外,系统中还应有一个段表寄存器,指出作业的段表始址和段表长度
注意:在一个进程中,段表只有1个,而页表可能有多个
在进行地址变换时,首先通过段表查到页表始址,然后通过页表找到页帧号,最后形成物理地址。进行一次访问实际需要三次访问主存, 这里同样可以使用快表来加快查找速度,其关键字由段号、页号组成,值是对应的页帧号和保护码。
段页式管理的地址空间是二维的
3.2 虚拟内存管理
3.2.1 虚拟内存的基本概念
2.局部性原理
快表、页高速缓存及虚拟内存技术从广义上来讲,都属于高速缓存技术。这个技术所依赖的原理就是局部性原理。 局部性原理既适用于程序结构,又适用于数据结构
局部性原理表现在以下两个方面:
(1)时间局部性
程序中的某条指令一旦执行,不久后该指令可能再次执行;某数据被访问过,不久后该数据可能再次被访问。产生时间局部性的典型原因是程序中存在着大量的循环操作。
(2)空间局部性
一旦程序访问了某个存储单元,在不久后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,因为指令通常是顺序存放、顺序执行的,数据也一般是以向量、数组、表等形式簇聚存储的。
时间局部性通过将近来使用的指令和数据保存到高速缓冲存储器中,并使用高速缓存的层次结构实现。空间局部性通常使用较大的高速缓存,并将预取机制 集成到高速缓存控制逻辑中实现。虚拟内存技术实际上建立了“内存-外存”的两级存储器结构,利用局部性原理实现高速缓存
3.虚拟存储器的定义和特征
在物理上扩展内存相对有限的条件下,可在逻辑上扩充内存,因此引入虚存的概念。虚拟内存使用外存上的空间来扩充内存空间,通过一定的换入/换出,使得整个系统在逻辑上能够使用一个远远超出其物理内存大小的内存容量。
基于局部性原理,在程序装入时,将程序的一部分装入内存,而将其余部分留在外存,就可启动程序执行。在程序执行过程中,当所访问的信息不在内存时,由OS将所需要的部分调入内存,然后继续执行程序。 另一方面,OS将内存中暂时不使用的内容换出到外存上,从而腾出空间存放将要调入内存的信息。 这样,系统好像为用户提供了一个比实际内存大得多的存储器,称为虚拟存储器。
之所以将其称为虚拟存储器,是因为这种存储器实际上并不存在,只是由于系统提供了部分装入、请求调入和置换功能后(对用户完全透明),给用户的感觉是好像存在一个比实际物理内存大得多的存储器。虚拟存储器的大小由计算机的地址结构决定,并不是内存和外存的简单相加。虚拟存储器有以下三个主要特征:
(1)多次性
多次性是指无须在作业运行时一次性地全部装入内存,而允许被分成多次调入内存运行
(2)对换性
对换性是指无须在作业运行时一直常驻内存,而允许在作业的运行过程中,进行换进和换出
(3)虚拟性
虚拟性是指从逻辑上扩充内存的容量,使用户看到的内存容量远大于实际的内存容量。
虚存空间的大小由什么因素决定?
虚存的大小要同时满足两个条件:
1.虚存的大小<=内存容量和外存容量之和,这是硬件的硬性条件规定的,若虚存大小超过了这个容量,则没有相应的空间来供虚存使用
2.虚存的大小<=计算机的地址位数能容纳的最大容量。假设地址是32位的,按字节编址,一个地址代表1B存储空间,则虚存的大小<=4GB。若虚存的大小超过4GB,则32位的地址将无法访问全部虚存,即4GB以后的空间被浪费了。
实际虚存的容量是取条件1和2的交集
虚存存在什么问题?
因为虚拟内存技术调换页面时需要访问外存,会导致平均访存时间增加,若使用了不合适的替换算法,则会大大降低系统性能。
4.虚拟内存技术的实现
虚拟内存技术允许将一个作业多次调入内存。采用连续分配方式时,会使相当一部分内存空间都处于暂时或“永久”的空闲状态,造成内存资源的严重浪费,而且也无法从逻辑上扩大内存容量。因此,虚拟内存的实现需要建立在离散分配的内存管理方式的基础上。
虚拟内存的实现有以下三种方式:
(1)请求分页存储管理
(2)请求分段存储管理
(3)请求段页式存储管理
不管哪种方式,都需要有一定的硬件支持。一般需要的支持有以下几个方面:
(1)一定容量的内存和外存
(2)页表机制(或段表机制),作为主要的数据结构
(3)中断机构,当用户程序要访问的部分尚未调入内存时,则产生中断
(4)地址变换机构,实现逻辑地址到物理地址的变换
3.2.2 请求分页管理方式
请求分页系统建立在基本分页系统基础之上,为了支持虚拟存储器功能而增加了请求调页功能和页面置换功能。 请求分页是目前最常用的一种实现虚拟存储器的方法。
在请求分页系统中,只要求将当前需要的一部分页面装入内存,便可以启动作业运行。在作业执行过程中,当所要访问的页面不在内存中时,再通过调页功能将其调入,同时还可通过置换功能将暂时不用的页面换出到外存上,以便腾出内存空间。
为了实现请求分页,系统必须提供一定的硬件支持。除了需要一定容量的内存及外存的计算机系统,还需要有页表机制、缺页中断机构和地址变换机构。
1.页表机制
请求分页系统的页表机制不同于基本分页系统,请求分页系统在一个作业运行之前不要求全部一次性调入内存,因此在作业的运行过程中,必然会出现要访问的页面不在内存中的情况,如何发现和处理这种情况是请求分页系统必须解决的两个基本问题。为此,在请求页表项中增加了4个字段:
(1)状态位P
用于指示该页是否已调入内存,供程序访问时参考
(2)访问字段A
用于记录本页在一段时间内被访问的次数,或记录本页最近已有多长时间未被访问,供置换算法换出页面时参考
(3)修改位M
标识该页在调入内存后是否被修改过
(4)外存地址
用于指出该页在外存上的地址,通常是物理块号,供调入该页时参考
2.缺页中断机构
在请求分页系统中,每当所要访问的页面不在内存中时,便产生一个缺页中断,请求OS将所缺的页调入内存。此时应将缺页的进程阻塞(调页完成唤醒),若内存中有空闲块,则分配一个块,将要调入的页装入该块,并修改页表中的相应页表项,若此时内存中没有空闲块,则要淘汰某页(若被淘汰页在内存期间被修改过,则要将其写回外存)
缺页中断作为中断,同样要经历诸如保护CPU环境、分析中断原因、转入缺页中断处理程序、恢复CPU环境等几个步骤。但与一般的中断相比,它有以下两个明显的区别:
(1)在指令执行期间而非一条指令执行完后产生和处理中断信号,属于内部中断。
(2)一条指令在执行期间,可能产生多次缺页中断。
3.地址变换机构
请求分页系统中的地址变换机构,是在分页系统地址变换机构的基础上,为实现虚拟内存,又增加了某些功能而形成的
在进行地址变换时,先检索快表:
(1)若找到要访问的页,则修改页表项中的访问位(写指令还需要重置修改位),然后利用页表项中给出的物理块号和页内地址形成物理地址
(2)若未找到该页的页表项,则应到内存中去查找页表,再对比页表项中的状态位P,,看该页是否已调入内存,未调入则产生缺页中断,请求从外存把该页调入内存
3.2.3 页面置换算法(决定应该换入哪页、换出哪页)
进程运行时,若其访问的页面不在内存中而需将其调入,但内存中已无空闲空间时,就需要从内存中调出一页程序或数据,送入磁盘的对换区。
选择调出页面的算法就称为页面置换算法。 好的页面置换算法应有较低的页面更换频率,即应将以后不会再访问或以后较长时间内不会再访问的页面先调出。
常见的置换算法有以下四种:
1.最佳(OPT)置换算法
最佳(Optimal,OPT)置换算法选择的被淘汰页面是以后永不使用的页面,或是在最长时间内不再被访问的页面,以便保证获得最低的缺页率。 然而,由于无法预知进程在内存下的若干页面中哪个是未来最长时间内不再被访问的,因而该算法无法实现。
2.先进先出(FIFO)页面置换算法
优先淘汰最早进入内存的页面,即在内存中驻留时间最久的页面。 该算法的实现只需把调入内存的页面根据先后次序链接成队列,设置一个指针总指向最早的页面。但该算法与进程实际运行时的规律不适应,因为在进程中,有的页面经常被访问。
FIFO算法还会产生所分配的物理块数增大而页故障数不减反增的异常现象,称为Belady异常。 之所以出现Belady现象是因为FIFO算法的置换特征与进程访问内存的动态特征是矛盾的,与置换算法的目标是不一致的(即替换较少使用的页面),因此,被它置换出去的页面并不一定是进程不会访问的。只有FIFO算法可能出现Belady异常,LRU和OPT算法永远不会出现Belady异常。
3.最近最久未使用(LRU)置换算法
选择最近最长时间未访问过的页面予以淘汰,它认为过去一段时间内未访问过的页面,在最近的将来可能也不会被访问。该算法为每个页面设置一个访问字段,来记录页面自上次被访问以来所经历的时间,淘汰页面时选择现有页面中值最大的予以淘汰。
注意LRU算法与OPT算法的不同,LRU算法根据各页以前的情况,是“向前看”的,而最佳置换算法则根据各页以后的使用情况,是“向后看”的。
LRU算法的性能较好,但需要寄存器和栈的硬件支持。 LRU是堆栈类的算法。堆栈类的算法不可能出现Belady异常。
LRU算法有两种可能的实现方法:
(1)系统维护一个页面链表,最近刚刚使用过的页面作为首结点,最久未使用的页面作为尾结点。每一次访问内存时,找到相应的页面,把它从链表中摘下来,再移动到链表之首。每次缺页中断发生时,淘汰链表末尾的页面
(2)设置一个活动页面栈,当访问某页时,将此页号压入栈顶,然后,考察栈内是否有与此页面相同的页号,若有则抽出。当需要淘汰一个页面时,总是选择栈底的页面,它就是最久未使用的
4.时钟(CLOCK)置换算法
LRU算法的性能接近于OPT算法,但实现起来比较困难,且开销大; FIFO算法实现简单,但性能差。OS的设计者试图用比较小的开销接近LRU算法的性能,这类算法都是CLOCK算法的变体。因为算法要循环扫描缓冲区,像时钟的指针一样转动,所以称为CLOCK算法。
简单的CLOCK算法给每帧关联一个附加位,称为使用位。当某页首次装入主存时,将该帧的使用位设置为1;当该页随后再被访问到时,其使用位也被置为1。对于页替换算法,用于替换的候选帧集合可视为一个循环缓冲区,并有一个指针与之相关联。当某一页被替换时,该指针被设置成指向缓冲区中的下一帧。当需要替换一页时,OS扫描缓冲区,以查找使用位被置为0的一帧。每当遇到一个使用位为1的帧时,OS就将该位重新置为0;若在这个过程开始时,缓冲区中所有帧的使用位均为0,则选择遇到的第一个帧替换;若所有帧的使用位均为1,则指针在缓冲区中完整地循环一周,把所有使用位都置为0,并停留在最初的位置上,替换该帧中的页。 由于该算法循环检查各页面的情况,因此称CLOCK算法,又称最近未用(Not Recently Used,NRU)算法
CLOCK算法的性能比较接近LRU算法,而通过增加使用的位数目,可以使得CLOCK算法更加高效。在使用位的基础上再增加一个修改位,则得到改进型CLOCK替换算法。 这样,每帧都处于以下4中情况之一:
(1)最近未被访问,也未被修改(u=0,m=0)
(2)最近被访问,但未被修改(u=1,m=0)
(3)最近未被访问,但被修改(u=0,m=1)
(4)最近被访问,被修改(u=1,m=1)
算法执行如下操作步骤:
(1)从指针的当前位置开始,扫描帧缓冲区。在这次扫描过程中,对使用位不做任何修改。选择遇到的第一个最近未被访问,也未被修改的帧(u=0,m=0)用于替换。
(2)若第(1)步失败,则重新扫描,查找最近未被访问,但被修改的帧(u=0,m=1)。选择遇到的第一个这样的帧用于替换。在这个扫描过程中,对每个跳过的帧,把它的使用位设置成0。
(3)若第(2)步失败,则指针将回到它的最初位置,且集合中所有帧的使用位均为0。重复第(1)步,并且若有必要,重复第(2)步,以便可以找到供替换的帧。
改进型CLOCK算法优于简单CLOCK算法的地方在于替换时首选没有变化的页。由于修改过的页在被替换之前必须写回,因而这样做会节省时间。
OS中任何经过优化而有效的页面置换算法都有一个原则,即尽可能保留曾经使用过的页面,而淘汰未使用的页面,认为这样可以在总体上减少换页次数。CLOCK算法只考虑到是否被访问过,因此被访问过的当然尽可能留下,未使用过的就淘汰;而改进型CLOCK算法对使用过的页面又做了细分,分为使用过但未修改过和使用过且修改过。因此,若有未使用过的页面,则当然首先把它换出,若全部页面都使用过,则当然优先把未修改过的页面换出。
LRU、FIFO和CLOCK算法的比较:
LRU算法和FIFO本质上都是先进先出的思路,只不过LRU是针对页面的最近访问的时间来进行排序,所以需要在每一次页面访问的时候动态地调整各个页面之间的先后顺序(有一个页面的最近访问时间变了);而FIFO是针对页面进入内存的时间来排序,这个时间是固定不变的,所以各页面之间的先后顺序是固定的。若一个页面在进入内存后没有被访问,那么它的最近访问时间就是它进入内存的时间。即若内存当中的所有页面都未曾访问过,那么LRU算法退化为FIFO算法
CLOCK算法是对LRU算法的近似,它用使用位模拟访问时间的先后顺序
3.2.4 页面分配策略
1.驻留集大小
对于分页式的虚拟内存,在进程准备执行时,不需要也不可能把一个进程的所有页都读入主存。因此,OS必须决定读取多少页,即决定给特定的进程分配几个页框。给一个进程分配的物理页框的集合就是这个进程的驻留集。 需要考虑以下几点:
(1)分配给一个进程的存储量越小,任何时候驻留在主存中的进程数就越多,从而可以提高处理机的时间利用效率。
(2)若一个进程在主存中的页数过少,则尽管有局部性原理,页错误率仍然会相对较高
(3)若页数过多,则由于局部性原理,给特定的进程分配更多的主存空间对该进程的错误率没有明显的影响
基于上述因素,现代OS通常采用3种策略:
(1)固定分配局部置换
它为每个进程分配一定数目的物理块,在整个运行期间都不改变。 若进程在运行中发生缺页,则只能从该进程在内存中的页面中选出一页换出,然后调入需要的页面。实现这种策略时,难以确定应为每个进程分配的物理块数目;太少会频繁出现缺页中断,太多又会使CPU和其他资源利用率下降。
(2)可变分配全局置换
这是最易于实现的物理块分配和置换策略,它为系统中的每个进程分配一定数目的物理块,OS自身也保持一个空闲物理队列。当某进程发生缺页时,系统从空闲物理块队列中取出一个物理块分配给该进程,并将欲调入的页装入其中。 这种方法比固定分配局部置换更加灵活,可以动态增加进程的物理块,但它会盲目地给进程增加物理块,从而导致系统多道程序的并发能力下降
(3)可变分配局部置换
它为每个进程分配一定数目的物理块,当某个进程发生缺页时,只允许从该进程在内存的页面中选出一页换出,因此不会影响其他进程的运行。若进程在运行中频繁地换页,则系统再为该进程分配若干物理块,直至该进程缺页率趋于适当程度;反之,若进程运行中的缺页率特别低,则可适当减少分配给该进程的物理块。 比起可变分配全局置换,这种方法不仅可以动态增加进程物理块的数量,还能动态减少进程物理块的数量,在保证进程不会过多地调页的同时,也保持了系统的多道程序并发能力。当然它需要更复杂的实现,也需要更大的开销,但对比频繁地换入/换出所浪费的计算机资源,这种牺牲是值得的
3.2.5 抖动
在页面置换过程中,一种最糟糕的情形是,刚刚换出的页面马上又要换入主存,刚刚换入的页面马上又要换出主存,这种频繁的页面调度行为称为抖动或颠簸。 若一个进程在换页上用的时间多于执行时间,则这个进程就在颠簸。
频繁发生缺页中断(抖动)的主要原因是,某个进程频繁访问的页面数目高于可用的物理页帧数目。 虚拟内存技术可在内存中保留更多的进程以提高系统效率。在稳定状态,几乎主存的所有空间都被进程块占据,处理机和OS可以直接访问到尽可能多的进程。然而,如果管理不当,那么处理机的大部分时间都将用于交换块,即请求调入页面的操作,而不是执行进程的指令,因此会大大降低系统效率。
3.2.6 工作集
工作集是指在某段时间间隔内,进程要访问的页面集合。基于局部性原理,可以用最近访问过的页面来确定工作集。 一般来说,工作集W可由时间t和工作集窗口大小△来确定。 例如,某进程对页面的访问次序如下:
实际应用中,工作集窗口会设置得很大,即对于局部性好的程序,工作集大小一般会比工作集窗口△小很多。 工作集反映了进程在接下来的一段时间内很有可能会频繁访问的页面集合,因此,若分配给进程的物理块小于工作集大小,则该进程就很有可能频繁缺页,所以为了防止这种抖动现象,一般来说分配给进程的物理块数(即驻留集大小)要大于工作集大小。
工作集模型的原理是,让OS跟踪每个进程的工作集,并为进程分配大于其工作集的物理块。落在工作集内的页面需要调入驻留集中,而落在工作集外的页面可从驻留集中换出。若还有空闲物理块,则可以再调一个进程到内存以增加多道程序数。若所有进程的工作集之和超过了可用物理块的总数,则OS会暂停一个进程,将其页面调出并将其物理块分配给其他进程,防止出现抖动现象。
3.2.7 地址翻译
设某系统满足以下条件:
(1)有一个TLB与一个data Cache
(2)存储器以字节为编址单位
(3)虚拟地址14位
(4)物理地址12位
(5)页面大小为64B
(6)TLB为四路组相联,共有16个条目
(7)data Cache是物理寻址、直接映射的,行大小为4B,共有16组
写出访问地址为0x03d4,0x00f1和0x0229的过程
因为系统以字节编址,页面大小为64B,则页内偏移地址为6位,虚拟页号8位,物理页号6位。因为TLB为四路组相联,共有16个条目,则TLB共有16/4=4组,因此虚拟页号中低2位为组索引,高6位为TLB标记。又因为Cache行大小为4B,因此物理地址中低2位为块偏移,Cache共有16组,接下来4位为组索引,剩下高6位作为标记
TLB、页表、data Cache内容如表所示:
先把16进制的虚拟地址转化为2进制形式:
得到每个地址的组索引和TLB标记,接下来就要找出每个地址的页面在不在主存中,若在主存中,则还要找出物理地址
对于0x03d4,组索引为3,TLB标记为0x03,查TLB,第3组中正好有标记为03的项,有效位为1,可知页面在主存中,对应的物理地址为0d(001101),再拼接页内地址010100,可得物理地址为0x354(001101010100)。
对于0x00f1,组索引为3,TLB标记为0x00,查TLB,第3组中没有标记为00的项,再去找页表,虚拟页号为0x03,页表第3行的有效位为1,可知页面在主存中,物理地址为02(000010),再拼接页内地址110001,可得物理地址为0x0b1(000010110001)
对于0x0229,组索引为0,TLB标记为0x02,查TLB,第0组中没有标记为02的项,再去找页表,虚拟页号为0x08,页表第8行的有效位为0,页面不在主存中,产生缺页中断
找出在主存中的页面的物理地址后,就要通过物理地址访问数据,接下来要找该物理地址的内容在不在Cache中,物理地址结构如表所示:
对于0x354,Cache索引为5,Cache标记为0x0d,对照Cache中索引为5的行,标记正好为0d,有效位为1,可知该块在Cache中,偏移0,即块0,可得虚拟地址0x03d4的内容为36H
对于0x0b1,Cache索引为c,Cache标记为0x02,对照Cache中索引为c的行,有效位为0,可知该块不在Cache中,要去主存中查找物理页号为2、偏移为0x31的内容
注意在整个查找过程中,查找顺序是从TLB到页表(TLB不命中),再到Cache和主存,最后到外存。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/153786.html