操作系统之存储器管理方式

存储器管理

存储器的层次结构

存储器设计为层次结构的原因

虽然存储器的容量不断扩大,但是随着软件在功能、规模、种类上的急剧增加,存储器仍然是一种比较稀缺的资源;对它的管理常常影响到存储器的利用效率,而且对系统性能也有一定的影响。一般的,对存储器有以下三条期望:

  1. 容量越大越好;
  2. 访问越快越好;
  3. 价格越低越好;
  4. 以上都能同时实现(不属于可期望范围之内)

常见存储器层次划分

对存储器的三条期望无法同时满足,所以需要在三者之间实现一种平衡,其目的还是提高系统性能(吞吐量和资源利用率);实现这一目的的方法就是设置缓存。层次结构中,低一层为高一层的缓存:低一层的存储介质通常容量更大、价格更低但是访问速度更慢(相比上一层而言),所以高一层+低一层=容量较大+访问较快+价格适中;常见的层次结构划分如下:

  1. 寄存器
  2. 高速缓存
  3. 主存储器
  4. 磁盘缓存
  5. 固定磁盘
  6. 可移动存储介质

其中,CPU寄存器和主存(前4级)也被称为可执行存储器;主要是系统对放置其中的信息的访问方式和赋存中信息的访问方式不同:进程通过一条load或者store指令可以实现对可执行存储器的访问,但是对辅存的访问通常涉及中断、设备驱动程序以及物理设备的运行,所以消耗的时间远远高于访问可执行存储器的时间;

程序的装入和链接

程序要在系统中运行,必须先将其装入内存,然后将其作为进程的一部分从而得以运行;程序的运行通常需要经过一下几个步骤(嗯,是通常):

  1. 编译:由编译程序对用户源程序进行编译,生成若干个目标模块;
  2. 链接:由链接程序将编译后生成的目标模块以及它们所需要的库文件链接在一起,形成一个完整的装入模块;
  3. 装入:由装入程序将装入模块装入内存;

这里需要注意的是,虽然这里指明了有三步,但是并没有说明这三步是什么时候执行;不同的执行时机反映不同的存储器管理策略;

程序的装入

再将一个装入模块装入内存时,通常有三种方式:

绝对装入方式

该装入方式中,程序的相对地址(逻辑地址)和实际内存中的地址(实际地址)是相同的,即程序中使用绝对地址,由于程序在编译或者汇编时给出逻辑地址,故该方法对程序员要求较高,而且一旦程序修改,可能所有的地址都需要修改。该装入方式适合系统较小,且仅能运行单道程序时使用;

可重定位装入方式(也称为静态重定位)

装入模块中使用相对地址。相对地址一般从0开始;采用可重定位装入方式装入模块后,会使装入模块中的所有逻辑地址与实际装入内存后的物理地址不同,所以需要有一个将逻辑地址转变为物理地址的过程,通常将该过程称为重定位;又因为地址变换通常是在进程装入时一次性完成,以后不再改变,故称为静态重定位方法;

该方法可用于多道程序环境下;

动态运行时装入

可重定位装入方式由于地址转换是在进程装入时一次性完成,以后地址不再改变,所以不允许程序和数据在内存中移动,这不利于提高存储器的利用效率。改变方法就是将地址转换推迟到指令执行时再进行,这样就允许程序和数据在内存中移动,便于系统对存储器的管理,为了使地址转换不影响指令的执行速度,这种方式需要一个重定位寄存器的支持。

程序的链接

在对目标模块进行链接时,根据进行链接的时间不同,可以分为三种链接方式:

静态链接

在程序运行之前,现将各个目标模块及它们所需要的库函数链接成一个完整的模块,以后不再拆开;这种事先链接的方式称为静态链接;进行静态链接时需要解决的问题有:相对地址的转换和变换外部调用符号;

装入时动态链接

在程序装入内存时,采取边装入边链接的方式,即在装入一个目标模块时,如果发生一个外部调用事件,将引起装入程序去找出相应的外部目标模块,将其装入内存。其优点:

  1. 便于修改和更新程序。对某一模块的修改只会引起该模块的重新编译和内部链接;
  2. 便于实现对目标模块的共享。采用动态链接方式时,OS可以将一个目标模块链接到几个应用模块上,从而实现对该模块的共享;而静态链接时,每个应用模块都将拥有共享模块的一份拷贝,浪费内存空间;

运行时动态链接

不需要将所有可能用到的模块都一次性装入内存即运行程序,当程序需要的模块不在内存时,OS将寻找该模块,将其装入内存。这样,凡是未用到的模块都将不用装入内存,提高了内存空间的利用率;即将连接过程推迟到程序运行时进行;

连续分配存储管

单一连续分配

单道程序环境下,存储区被分为系统区和用户区两部分,通常系统区位于低地址部分,供OS使用;用户区仅装有一道用户程序,整个用户区由该程序独占,这种方法称为单一连续分配;

固定分区分配

多道程序环境下,将内存的用户空间划分为若干个固定大小的分区,然后为用户程序分配;在划分上,可以划分为相同大小的若干块,也可以划分为不同大小的若干块;一般来说相同大小的划分常用于专用系统(提前得知每个程序所需要的内存大小相差不大),不同大小的划分更为常见;一般划分为多个较小的分区、适量的中等分区、少量的大分区;

通常将分区块按大小将其排队,并建立分区使用表,每一个表项包括分区块的内存起始地址、状态、大小、分区号等信息;

动态分区分配

动态分区分配又称为可变分区分配,它根据进程的实际需要为之分配内存空间,在实现动态分区分配时需要解决以下三个问题:使用的数据结构、分区分配方法、分区的分配和回收操作。

  1. 使用的数据结构

    用以描述空闲分区,常见的有:

    空闲分区表:每一个表项记录分区号、分区大小、 分区的起始地址;

    空闲分区链:每一个空闲分区起始部分设置一些用于控制分区分配的信息以及用于链接各分区所用的前向指针,在分区尾部则设置一个后向指针,通过前后向指针,将所有空闲分区链接成一个双向链表;

  2. 分区分配算法

    常见的有四种顺序式搜索算法和三种索引式算法

    1. 顺序式搜索算法
      1. 首次适应算法(First Fit)
      2. 循环首次适应算法(Next Fit)
      3. 最佳适应算法(Best Fit)
      4. 最坏适应算法(Worst Fit)
    2. 索引式算法
      1. 快速适应算法(Quick Fit)
      2. 伙伴系统(Buddy System)
      3. 哈希算法
  3. 分区的分配和回收操作

    分区分配操作中涉及两个操作:分区的查找和切割;所谓查找就是指寻找一个大小合适的空闲分区块;切割就是指在找到这样的空闲分区块之后,按照切割条件判断是否允许将这一空闲分区块的空间分配给请求进程;

    分区回收操作主要涉及的问题就是空闲块之间的合并,不同的布局环境有着不同的操作:

    1. 回收区X同上一块空闲区A相邻:合并A和X并修改A的大小;
    2. 回收区X同下一块空闲区B相邻:合并A和B并修改X的大小;
    3. 回收区X同时和A、B相邻:合并A、X、B并修改A的大小;
    4. 回收区X和空闲块不相邻:新建一个空闲表项,填充相关信息后将其首地址插入到空闲链的适当位置;

动态可重入分区分配

连续分配方式的一个重要特点就是,一个系统或者用户程序必须装入一块连续的内存空间,当一台计算机连续运行一段时间后,其内存空间将产生不少小的分区,而缺乏大的空闲分区;这样即便这些分散的小空间的总量大于要装入程序的需求,但是由于他们不相邻,所以也就无法装入该程序;这些小的分区就是“碎片”。若要利用这些“碎片”,可采用的一种方式就是将内存中的所有作业进行移动以实现将小碎片整合在一块的目的,这一过程称为“紧凑”;但是,每经过一次移动后,程序在内存中的位置就发生了变化,需要对程序的地址加以修改,但是这相当麻烦,而且大大地影响到系统的效率;所以,希望通过一种方法,只移动程序在内存中的位置,但是并不需要对相对地址进行修改;动态重定位就是这样的一种方法;其实,动态重定位和程序的动态运行时装入有着相似的原理:将地址转换延迟到指令执行时再进行:

为使地址的转换不影响到系统指令的执行速度,必须有硬件地址变换机构的支持,即在系统中增设一个重定位寄存器,用它来记录数据在内存中的起始地址。程序中的指令期地址是有相对地址与重定位寄存器中的地址相加得到的,当系统进行了“紧凑”之后,只需要修改寄存器中的内容,而不需要对所有的地址进行变换。

分页存储管理方式

页的引入及其组织形式

分页管理系统将进程的逻辑地址空间划分为若干个页,对页进行编号;同样的将物理内存空间也划分为若干块并编号。在为进程分配内存空间时,以块为单位,将进程中的若干页装入可以不相邻的若干物理块中。由于进程的最后一页经常装不满,从而形成了页内碎片;

页面大小:页面大小如果选的过于小,那么系统就会有很多页,会增加系统管理上的难度——维护页表、对页面进行换出换入都比较耗性能;页面大小如果选择的过于大,那么系统就会产生较多的业内碎片,不利用提高存储器的利用率;通常大小选择为1kb-8kb;

分页地址中,地址由两部分内容组成,第一部分是页号,第二部分是页内偏移,即页内地址;其中偏移量占12为,页号占20位;

页表

分页系统中,允许程序离散分布在内存中的物理块中,为了管理这些页面,系统将维护一个页表,从而建立页面到物理块的关联;常在页表结构里设置对页面内容的访问控制,

地址变换机制及快表

为了将用户地址空间里的逻辑地址转换为内存空间中的物理地址,需要一个地址变换机制将分页地址变换为物理地址;由于该转换执行频率很高,所以页表功能常常使用一组寄存器来实现,即将页表存放在寄存器中,但是随着程序复杂度的提高,页表长度也变得很大,所以就将页表存放在内存中而在寄存器里存放页表首地址和页表长度;

当进程需要访问某个逻辑地址中的进程时,地址变换机制将识别页表地址中的页号和页内偏移部分,然后以页号索引检索页表,不过在此之前应有范围检查,以判断是否发生越界错误;如果没有错误,那么使用页表始地址与页号和页表项的乘积相加,得到页表项在页表中的地址,从而读出物理地址,将之装入物理地址寄存器中,之后配合使用页表地址中的页内偏移部分,即可完成地址转换;

从上面的转变过程来看,CPU访问一个物理地址,需要两次访问内存:一次是查找页表,得到物理地址,另一次是真正访问地址中的内容,这样效率就降低了近一半;以这样的代价换取存储空间的利用率提升是不值得,为了提高地址转换效率,一种合理方法便是:缓存,基于局部性原理的缓存机制,即快表;

快表是地址转换结构中的一种具有并行查找能力的特殊高速缓冲寄存器,又称为联想寄存器;当CPU给出有效地址后,地址转换结构就将页号送入高速缓冲寄存器,进行一次查找,如果有相匹配的页号,表示要访问的页表数据在快表里,可直接在快表里获得对应的物理块号。如果没有找到,则需要再次访问内存中的页表以获得物理块地址,找到该地址后就将其放入快表的一个寄存器单元里,即修改快表。如果快表已满,则需要找到一个寄存单元执行换出。

据统计,从快表中找到所需表项的概率高达90%以上,这样由于增加了地址变换机构而造成的速度损失可以减小到10%以下,达到可以接受的程度。

两级页表和多级页表

由于现代计算机系统支持非常大的逻辑空间,导致在系统中难以找到一块连续的内存空间存放页表;关于这个问题,有两种解决方案:一是使用离散的方式存储页表;二是将需要的页表存放在内存中,不需要的存放在外存中;

两级页表:

将页表进行分页离散存储之后,就需要为页表再建立一张索引表(页表为物理内存块的检索表),这张表即为外层页表。该表项结构分为三部分:外层页号+外层页内地址+页内地址;其地址转换过程如下:首先根据外层页号m得到页面所在首地址p(p=m*外层页表项+外层页表首地址),由此地址p和外层页内地址x得到页面所在物理块的首地址n,由n和页内地址一起得到最终指令的物理地址;这样,获得一条指令的内容就需要三次访问内存了。一次是访问外表,得到页面所在物理块的首地址;第二次是根据页面首地址和外层页内地址得到实际指令的首地址;第三次就是访问实际数据了。

多级页表和两级页表类似,理解多级页表的概念,可类比多重数组;

反置页表

页表是为所用页面而设计的,页表项中存储的是页面对应的物理地址;反置页表则是为内存中的物理块编号,使用页表来管理物理块,其中页表项中存储的是页面以及进程的标记,表示该物理块所属于哪个进程,存储的是那个页面的数据;有些系统使用反置页表来管理内存;但是还有一个问题就是,反置页表只能建立已调入内存中的页面和物理块之间的关联,但是一个进程的所有页面不一定全部会调入内存,这里就涉及页面是否调入内存的管理。

一般来说,使用Hash表来管理反置页表来提高检索速度,但是使用Hash表则需要解决地址冲突的问题,该问题有很多种解决方法,这里不做叙述;

分段存储管理方式

从单一连续分配方法发展到固定分区分配再到动态分区分配,再到分页存储管理,推动这一系列改进的动力是提高存储空间的利用率,而引入分段存储管理方式的目的在于便于编程、信息共享、信息保护、动态增长和动态链接的实现:

  1. 分页系统中,页面内的代码是空间地址上的关系而不是逻辑内聚的,编程时希望引入的是一块逻辑相关的代码;
  2. 由于分页系统中页中的内容不代表逻辑模块,所以不便基于分页管理实现逻辑代码共享;
  3. 由于具有逻辑体系的代码段可能分布再多个页面,这样对这些代码段的保护就难以进行;
  4. 页面往往具有固定的大小,但是程序中很容易出现数据大小未知的情况,这样使用页面管理就不方便;
  5. 运行时动态链接的实现,需要以模块为单位进行链接,即目标程序应该作为链接的基本单位,所以分页系统对此的支持也不是很好;

上面提到的5个方面,使用分段存储都可以得到满意的解决;

分段概念及其内存组织形式

在分段存储管理方式中,内存空间被划分为若干个段,每个段定义了一组逻辑信息,每个段使用连续的内存空间,各个段的长度由对应的逻辑信息组的长度决定,因此段的长度往往不等;由于作业的地址空间被划分为若干段,所以地址空间具有二维性,而分页系统中,地址空间的分配具有一维性;

分段存储管理中,地址空间仍然有两部分组成,一部分为段号,另一部分为段内地址;同样,为了管理这些段,内存中设有段表。其中段表项记录每个段的始地址和该段的长度;

地址转换机制

地址转换方式基本同分页系统:首先从指令内容中得到短号,并由此定位到段表项;由段表项的内容获得该段的物理地址,之后由段内地址配合计算得出指令或者数据的物理地址;

分段、分段系统中的信息共享

虽然分页系统中也可以实现对信息的共享,但是,如果共享的信息过于庞大,那么对应的页的数目就会很多,这样在每个共享该数据的进程控制块里存储这些共享的页面就会使用不少的空间,使得PCB的体积过于庞大;但是使用分段系统的话,由于逻辑相关的代码被存储在一个段里,所以只需要存储一个段地址以及段长度即可,换言之,分段系统中存储的是共享信息块的数量,而分页系统则存储的是共性信息块的体积

猜你喜欢

转载自blog.csdn.net/slx3320612540/article/details/81190883