Chapter4存储器管理

存储器的多层结构
CPU寄存器(寄存器),主存(高速缓存,主存储器,磁盘缓存),辅存(固定磁盘,可移动存储介质)
寄存器,高速缓存,主存储器,磁盘缓存属于系统存储管理的管辖范围,掉电后其中的信息都不存在了。
寄存器和主存储器又称可执行存储器。进程可以在很少的时钟周期内使用一条load或store指令对其进行访问。但对辅存的访问则需要通过I/O设备实现。
操作系统的存储管理负责对可执行存储器的分配,回收,以及提供在存储层次间数据移动的管理机制。
设备和文件管理则根据用户的需求,提供对辅存的管理机制。


磁盘缓存与高速缓存不同,它本身并不是一种实际存在的存储器,而是利用主存的部分存储空间暂时存放从磁盘读出或写入的信息。
主存也可以看作是辅存的高速缓存,因为辅存的数据必须复制到主存中才能使用,并且数据也必须先存到主存,才能输出到辅存。


程序的装入和链接
源程序编译后形成若干个目标模块。
链接程序将目标模块和它们所需要的库函数链接在一起,形成一个完整的装入模块。
装入程序将装入模块装入内存。


程序的装入
1.绝对装入方式
当计算机系统很小,且只能运行单道程序时
事先已知进程会驻留在内存的什么位置
编译后直接产生指令和数据的绝对地址(即物理地址)的目标代码
2.可重定位装入方式
多道程序环境下,编译程序不可能预知经编译后所得的目标模块应放在内存的什么位置。
若干个目标模块的起始地址通常都是0,程序中的其它地址(指令地址数据地址)都是相对于起始地址计算的
在装入时对目标程序中指令和数据地址的修改过程称为重定位。
地址变换通常是在进程装入时一次完成的,以后不再改变,所以又称静态重定位。
pic
3.动态运行时的装入方式
在运行过程中进程在内存中的位置可能会经常改变,比如在具有对换功能的系统中,每次被换入后的位置通常是不同的。
把地址转换推迟到程序真正要执行时才进行,因此装入内存后的所有地址都仍是逻辑地址。
需要用到重定位寄存器。


程序的链接
1.静态链接方式
在程序运行之前,先将各目标模块及它们所需的库函数链接成一个完整的装配模块,以后不再拆开。
将多个模块连接成一个装入模块时,需解决以下两个问题
1)对相对地址进行修改(修改起始地址)
2)变换外部调用符号(把C改成L+M)
这种先进行链接所形成的一个完整装入模块又称为可执行文件,通常都不再拆开,要运行 时可直接将它装入内存。

2.装入时动态链接
在装入内存时,采用边装入边链接的方式。
即在装入一个目标模块时,若发生一个外部模块调用事件,将引起装入程序去找出相应的外部目标模块,并将它装入内存。
优点:
1)便于修改和更新
因为各目标模块是分开存放的
2)便于实现对目标模块的共享
将一个目标模块链接到几个应用模块上,实现多个应用程序对该模块的共享。

3.运行时动态链接
应用程序在运行时,每次要运行的模块可能是不同的,装入时也不知道运行的是哪个模块。
在执行过程中,当发现一个被调用模块尚未装入内存时,立即由OS去找到该模块,并将它装入内存,将其链接到调用者模块上。
加快程序的装入过程,节省大量的内存空间。


连续分配存储管理方式(进程是整体装入的)
为用户程序分配一个连续的地址空间,即程序中代码或数据的逻辑地址相邻,体现在内存空间分配时物理地址的相邻。

1.单一连续分配
单道程序环境下,把内存分为系统区和用户区
整个内存的用户空间由该程序独占

2.固定分区分配
将整个用户空间划分为若干个固定大小的区域,在每个分区只装入一道作业。
相对于分页来说分区很大
划分分区的方法
1)分区大小相等
多用于利用一台计算机同时控制多个相同对象的场合。
2)分区大小不等
最常在该系统中运行的作业大小进行调查,根据用户的需要来划分。
内存分配
将分区按其大小进行排队,建立一张分区使用表,包括每个分区的起始地址,大小及状态。

3.动态分区分配
又称可变分区分配
根据进程的实际需要,动态地为之分配内存空间。
1)空闲分区表
2)空闲分区链
为了检索方便,在分区尾部重复设置状态位和分区大小表目
pic

分区分配操作
1)分配内存
若多余部分太小,可不再切割,将整个分区分配给请求者;否则,从该分区中按请求的大小划分出一块内存空间分配出去,余下部分仍留在空闲分区链中。然后将分配区的首址返回给调用者。
pic
2)回收内存
系统根据回收区的首址,从空闲区链(表)中找到相应的插入点。
然后根据回收区前后的空闲情况有四种不同的操作。

  • 回收区与插入点的前一个空闲分区F1相邻接——修改F1的大小
  • 回收区与插入点的后一个空闲分区F2相邻接——合并形成新的空闲分区,用回收区的首址作为新空闲区首址,大小为两者之和。
  • 回收区同时与插入点前后两个空闲分区邻接——修改F1大小为三区之和,取消F2表项
  • 前后都不空闲——建立一个新表项,并根据首址插入到空闲链中适当位置

动态分区算法
1.基于顺序搜索的动态分区分配算法
依次搜索空闲分区链上的空闲分区,寻找一个大小能满足的分区。
1)首次适应算法FF
空闲分区链以地址递增的次序链接
每次都从链首开始顺序查找
倾向于优先利用内存中低址部分的空闲分区,这样地址部分就会产生很多碎片,但每次查找又是从低址部分开始的,这样就增加了开销。

2)循环首次适应算法NF
从上次找到的空闲分区的下一个空闲分区开始找(注意不是从上次找到的分割后剩下的那部分开始)
设置一起始查找指针,采用循环查找方法
使内存中的空闲分区分布更均匀,但会导致缺乏大空闲分区。

3)最佳适应算法BF
把能满足要求的最小的空闲分区分配给作业
按容量从小到大形成空闲分区链
会留下很多难以利用的碎片

4)最坏适应算法WF
总是挑最大的分区划一部分给作业。
查找效率高
会导致缺乏大的分区,但不易产生碎片

2.基于索引搜索的动态分区算法
1)快速适应算法
又称分类搜索法
将空闲分区根据其容量大小进行分类
对于每一类具有相同容量的所有空闲分区,单独设立一个空闲分区链表
设立一张管理索引表,每个索引表项对应了一种空闲分区类型以及该类型空闲分区链表表头的指针。
根据进程的长度,从索引表中去找能容纳它的最小空闲区链表,从链表中取下第一块进行分配即可。
不会对任何分区产生分割。
为了有效合并分区,在分区归还时算法比较复杂。

2)伙伴系统
无论已分配分区或空闲分区,其大小均为2k(1<=k<=m,2m为整个可分配内存的大小)
不同大小的空闲分区形成了多个空闲分区链表。
当需要分配一个长度为n的存储空间时
先计算i,2(i-1)i
然后在2^i的链表中找
若找到空闲的就分配给进程
否则,在2^(i+1)的链表中找
若找到,则把这个大小为2^(i+1)的分区分为大小相等的两个分区,称为一对伙伴
其中一个分区用于分配,另一个加入到2^i的链表中
若没找到就去2^(i+2)的链表中找,然后分割两次
(最坏的情况可能要对2^k的空闲分区进行k次分割)
回收时也可能要进行多次合并
对于一个大小为2^k地址为x的内存块,其伙伴块的地址为(?)
pic

3)哈希算法
分类搜索和伙伴系统中,如果空闲分区分类比较细,搜索索引表的表项的时间开销也会很大。
所以
就利用哈希快速查找的优点,以及空闲分区在空闲分区表中的分布规律建立哈希函数,构造一张以空闲分区大小为关键字的哈希表。每个表项记录了一个对应的空闲分区链表表头指针。


动态可重定位分区分配

紧凑
将内存中的所有作业进行移动,使它们全都相邻接
每次紧凑后都要对移动了的程序或数据进行重定位。进行地址的修改,这样很麻烦还影响系统效率,所以采用动态重定位方法。

动态重定位
重定位寄存器,存放程序(数据)在内存中的起始位置。
地址变换过程是在程序执行期间,随着每条指令或数据的访问自动进行。
动态重定位分区分配算法与动态分区分配算法基本相同,只是增加了紧凑功能。当找不到足够大的分区,判断空闲分区总和是否够大,如果够的话就进行紧凑。


对换
把内存中暂时不能运行的进程或暂时不用的程序和数据换到外存上。
1)整体对换
以整个进程为单位,作为处理机中级调度。
2)页面(分段)对换
以进程的一个“页面”或“分段”为单位进行,部分对换。

在具有对换功能的OS中通常把磁盘空间分为文件区和对换区。
对换空间只占用磁盘空间的一小部分,用于存放从内存换出的进程。这部分应该首要考虑提高进程换入换出的速度,然后才是提高文件存储空间的利用率。所以采用连续分配方式,较少考虑外存中的碎片问题。
应设置一个数据结构用于记录外存对换区中的空闲盘块的使用情况,每个表项中分别用盘块号和盘块表示对换区的首址及其大小。
对换空间的分配与回收与动态分区方式时的内存分配与回收类似。


进程的换入与换出
1)换出(内存不够时)
选择被换出的进程。首先选择处于阻塞或睡眠状态的进程,有多个的话就选优先级最低的。有时为了防止低优先级进程被调入内存之后又很快被换出,还需考虑进程在内存的驻留时间。
只能换出非共享的程序和数据段。
申请对换空间-》启动磁盘,将该进程的程序和数据传送到磁盘的对换区上-》回收该进程所占用的内存空间(修改PCB和内存分配表等)
重复执行直到内存中再无阻塞进程。
2)换入(定时执行)
查看PCB集合中所有进程的状态,找出就绪且已换出的进程,有多个时选择被换出时间最久的(但有个下限时间,不能刚被换出又马上换入)
申请内存-》将进程从外存调入内存
若申请失败则需将某些进程换出。
重复执行知道没有就绪且换出的进程或者没有足够的内存来换入进程(即使把阻塞的进程换出去也不够)
交换一个进程需要很多时间,所以这样频繁的换不好。
目前用的较多的对换方案是,在处理机正常运行时,并不启动对换程序。但如果发现有许多进程在运行时经常发生缺页且显示出内存紧张的情况,才启动对换程序,将一部分进程调至外存。


离散分配方式
不同于连续分配,这里允许将一个进程直接分散地装入到许多不相邻接的分区中
根据所分配地址空间的基本单位不同又分为以下三类
1)分页存储管理方式
将用户程序的地址空间分为若干个固定大小的区域,“页”;将内存空间分为若干个“块”;页和块的大小相同;可将用户程序的任一页放入任一物理快中。
2)分段存储管理方式
用户程序的地址空间分为若干大小不同的段(每段定义一组相对完整的信息);在存储器分配时以段为单位。
3)段页式存储管理方式


分页存储管理方式
页内碎片:进程的最后一页经常装不满一块,形成了不可利用的碎片。
页面太小会导致页表过长,太大会导致页内碎片增大,一般选择2的幂,1KB-8KB
地址结构:
([31 页号P 12][11 位移量W(页内地址) 0])
12位页内地址-》每页的大小为4KB
20位页号-》地址空间最多允许有1M页
逻辑空间地址A,页面大小L,页号P,页内地址d
P=int(A/L),d=A%L
页表:记录了相应页在内存中对应的物理块号(还可能有存取控制字段)


地址变换机构
将逻辑地址中的页号转换为内存中的物理块号,地址变换任务借助页表来完成。
1)基本的地址变换机构
页表驻留在内存,在系统中只设置一个页表寄存器,存放页表在内存中的始址与长度。
进程未执行时,页表的始址和长度存放在本进程的PCB中,当调度程序调度到某进程时,才将这两个数据放到页表寄存器。
当进程要访问某个逻辑地址的数据时,分页地址变换机构会自动将有效地址分为页号和页内地址两部分;
将页号与页表长度进行比较,若越界则产生越界中断,否则;
将页表始址与页号和页表项长度的乘积相加,得到该项在页表中的位置,就能得到物理块号,将其放入物理地址寄存器中;
将页内地址送入物理寄存器的快内地址字段。
pic

2)具有快表的地址变换机构
上述变换需要两次访问内存(访问页表与访问数据)
可在地址变换机构中增设一个具有并行查找能力的特殊高速缓冲器(快表/联想寄存器),存放当前访问的那些页表项
先将页号送入高速缓冲器中,若在快表中未找到对应的页表项,则再访问内存中的页表,然后还要重新修改快表,而且如果不够的话还要考虑换出一个页表项。
pic


访问内存的有效时间
从进程发出指定逻辑地址的访问请求,经地址变换,到在内存中找到对应的实际物理地址单元并取出数据,所需的总时间。EAT
假设访问一次内存的时间为t
基本变换机构 EAT=2t
假设快表的命中率为a,查找快表的时间为λ
EAT=aλ+(t+λ)(1-a)+t(注意即使没有命中也是花了查找快表的时间的)


两级和多级页表
对于一个具有32位(232B)逻辑地址空间的分页系统,规定页面大小为4KB(212B),则每个进程页表中的页表项数可达1M(2^20),又每个页表项占用一个字节,则每个进程光页表项就要占1MB。
有两个方案:(1)以离散的方式分配页表(2)只将当前需要的部分页表项调入内存,其余页表项仍驻留在磁盘上,需要时在调入。(虚拟存储)

1.两级页表
将页表进行分页
为离散分配的页表再建立一张页表,称为外层页表(记录内层页表在哪个块上,内层页表记录页在哪个块上)(但其实没有内层页表这种说法,直接叫页面)
([31 外层页号 22][21 外层页内地址 12][11 页内地址 0])
同样需要增设一个外层页表寄存器。
pic
这样就解决了没有一大片连续存储空间的问题,但为了是页表所占内存变小,还需要做以下的事情。
对于正在运行的进程,必须将其外层页表调入内存,而只需要调入部分页表;
为了表征某页的页表是否已经调入内存,还应在外层页表项中增设一个状态位S,若其值为0,则表示该页表分页不在内存中,产生一个中断信号,请求OS将该页表分页调入内存。

2.多级页表
对于64位机器,两级页表是很难办到的,外层页表项还是会很多。


分段存储管理方式
程序可分为若干个段,每个段大多是一个相对独立的逻辑单位;实现和满足信息共享,信息保护,动态链接以及信息的动态增长等需要,也都是以段为基本单位的。
分段存储管理更符合用户和程序如下的需求:方便编程;信息共享;信息保护;动态增长;动态链接。

作业的地址空间被划分为若干个段;每个段都从0开始编址,并采用一段连续的地址空间。
([31 段号 16][15 段内地址 0]) 64K个段,每个段的长度为64KB
编译程序能自动根据源程序的情况产生若干个段。

段表:每个表项记录了该段在内存中的起始地址(基址)和段的长度
设置了段表寄存器,同样也类似分页系统里一样会增设联想寄存器
pic

分页与分段的主要区别
1)页是信息的物理单位,段是信息的逻辑的单位。
2)页的大小固定且由系统决定。在硬件结构上就把逻辑地址分为页号和业内地址两部分。
3)分页的用户程序地址空间是一维的,只要给出一个虚拟地址操作系统会自己分出页号和页内地址。分段中是二维的,需要明确给出段号和段内地址。


信息共享
1)分页系统
为页表项指定相同的块号
2)分段系统
为段表项指定相同的基址
可重入代码,允许多个进程同时访问,所以不能被某个进程改变。但代码执行过程中是有可能被改变的,因此在每个进程中都配有局部数据区,把执行中有可能改变的部分拷贝到改数据区,在进程执行时,只需对该数据区(进程私有)中的内容进行修改,并不去改变共享的代码(可重入代码)。


段页式存储管理方式(先段后页)
先将用户程序分成若干个段,在把每个段分成若干页。
([段号S][段内页号P][页内地址W])
段表中存放的是页表始址和页表长度
pic

地址变换过程
pic
为了获得一条指令或数据需要三次访问内存。所以增设了高速缓冲寄存器,同时利用段号和页号去检索它,若找到了就可以获得相应物理块号。

你可能感兴趣的:(Chapter4存储器管理)