操作系统内存存储管理

来源:互联网 发布:网络商务英语 编辑:程序博客网 时间:2024/05/20 23:37

操作系统内存存储管理

固定分区存储管理

又称定长分区或静态分区模式,是满足多道程序设计需要的最简单的存储管理技术。

静态分区存储管理是预先把可分配的主存储器空间分割成若干个连续区域,每个区域的大小可以相同,也可以不同。为了说明各分区的分配和使用情况,存储管理需设置一张“主存分配表”。主存分配表指出各分区的起始地址和长度,表中的占用标志位用来指示该分区是否被占用了,当占用的标志位为“0”时,表示该分区尚未被占用。进行作业分配时总是选择那些标志为“0”的分区,当某一分区分配给一个作业后,则在占用标志栏填上占用该分区的作业名。

基本思想:给进入主存的用户作业划分一块连续存储区域,把作业装入该连续存储区域,若有多个作业装入主存,则它们可并发执行。

如表可能是一个区域大小不完全相同的固定分区的主存分配表:

分区号 起始地址 长度 占用标志 1 8K 8K 0 2 16K 16K Job1 3 32K 16K 0 4 48K 64K 0 5 64K 32K Job2 6 96K 32K 0

图6-4 固定分区存储管理的主存分配表

对于使用大小相等的固定分区有两个难点:程序可能太大而不能放到一个分区中,内存的,利用率很低。由于被装入的数据块小于分区大小,从而导致分区内部有浪费现象,成为“内部碎片”。对与大小不等的分区策略,最简单的方法就是把每个进程分配到能够容纳它的最小分区中。

目前已经基本上没有什么场合使用固定分区。

优势:实现简单,只需要极少的操作系统开销
缺点:有内部碎片,对内存的使用不充分,活动进程的最大数目是固定的。

动态分区存储管理

可变分区方式是按作业的大小来划分分区。当要装入一个作业时,根据作业需要的主存量查看主存中是否有足够的空间,若有,则按需要量分割一个分区分配给该作业;若无,则令该作业等待主存空间。由于分区的大小是按作业的实际需要量来定的,且分区的个数也是随机的,所以可以克服固定分区方式中的主存空间的浪费。

随着作业的装入、撤离,主存空间被分成许多个分区,有的分区被作业占用,而有的分区是空闲的。当一个新的作业要求装入时,必须找一个足够大的空闲区,把作业装入该区,如果找到的空闲区大于作业需要量,则作业装入后又把原来的空闲区分成两部分,一部分给作业占用了;另一部分又分成为一个较小的空闲区。当一作主行结束撤离时,它归还的区域如果与其它空闲区相邻,则可合成一个较大的空闲区,以利大作业的装入。

一 对于主存空间的分配和回收:

系统初启时,整个用户区可看作一个大的空闲区。当作业要求装入时,根据作业对内存需求量,从空闲区中划出一个与作业大小一致的分区来装入该作业,剩余部分仍为空闲区。当空闲区能满足需求时(即空闲区长度>=作业长度),作业可装入;否则,作业暂时不能装入,需要等待主存资源。下图是可变分区存储空间分配示意。

可变分区存储空间分配示意

装入内存的作业执行结束后,所占分区被收回成为一个空闲区,这个空闲区又可用于装入其他作业。随着作业不断装入和撤离,内存空间被分成许多分区,有的被作业占用,有的空闲。如下图所示。可见内存中空闲区数目和大小是在不断变化的。

可见内存中空闲区数目和大小

实际上,系统内设置一张“ 空闲区表 ”,用来记录空闲区的起始地址和长度。当有作业要装入内存时,在空闲区表中找一找“ 未分配 ”的栏目,从中找出一个能容纳作业的空闲区。若空闲区大于作业的长度时则被分成两部分,一部分分配给作业;另一部分仍作为空闲区登记在表中。分配给作业的空闲区该栏目状态会改为“空”状。当有作业撤离收回所占分区后,应把收回区域的起始地址和长度登记在状态为“空”的栏目中,且将状态改为“未分配”。如果收回的区域正好和某一空闲区相邻,则应将其合并成一个分区后登记。

作业撤离装入示意

优缺点:动态分区可克服固定分区方式中的内部碎片化问题,但是由于,作业的不断申请和释放内存,必然又会导致外部碎片问题。

可变分区内存分配算法

1.首次适应算法

  每次分配时,总是顺序查找未分配表,找到第一个能满足长度要求的空闲区为止。分割这个找到的未分配区,一部分分配给作业,另一部分仍为空闲区。优点是实现简单,缺点是可能将大的空闲区分割成许多小的空闲区,形成许多不连续的“碎片”。碎片长度可能不能满足后续作业要求,降低了内存利用率。
  改进方法,可把空闲区按地址顺序从小到大登记在空闲区表中,有利于大作业。问题是归还空区时须按地址插入表中适当位置。

首次适应算法

2.最佳适应算法

  按作业要求从所有空闲区中挑选一个能满足要求的最小空闲区,这样保证不去分割一个更大的区域,使装入大作业时比较容易得到满足。实现办法:将空闲区按长度以递增次序登记在表中,分配时按空闲区表顺序查找即可。缺点是可能碎片更小而无法使用。回收时也要按长度插入。

最佳适应算法

3.最坏适应分配算法

挑选一个最大的空闲区分割给作业使用,这样可使剩下的空闲区不至于太小,这种算法对中、小作业是有利的。采用这种分配算法时可把空闲区按大小以递减顺利排列,查找时总是从最大的一个区开始。按这种方法,在收回一个分区时也必须对表格重新排列。

最坏适应分配算法

基本分段式存储管理

在分段存储管理方式中,作业的地址空间被划分为若干个段,每个段定义了一组逻辑信息。例如,有主程序段MAIN、子程序段X、数据段D及栈段S等,每个段都有自己的名字。为了实现简单起见,通常可用一个段号来代替段名,每个段都从0开始编址,并采用一段连续的地址空间。段的长度由相应的逻辑信息组的长度决定,因而各段长度不等。整个作业的地址空间由于是分成多个段,因而是二维的,亦即,其逻辑地址由段号(段名)和段内地址所组成。

分段地址中的地址具有如下结构:

数据的逻辑地址

如上结构中,段号为16位,段内偏移地址为16位,故在该地址结构中,允许一个作业最长有 64 K个段,每个段的最大长度为64 KB。 

在页式系统中,逻辑地址的页号和偏移量对用户是透明的,但在段式系统中,段号和段内偏移量必须由用户显示提供,在高级程序设计语言中,这个工作由编译程序完成。编译程序能自动地根据源程序的情况而产生若干个段。例如,Pascal编译程序可以为全局变量、用于存储相应参数及返回地址的过程调用栈、每个过程或函数的代码部分、每个过程或函数的局部变量等等,分别建立各自的段。类似地,Fortran编译程序可以为公共块(Common block)建立单独的段,也可以为数组分配一个单独的段。装入程序将装入所有这些段,并为每个段赋予一个段号。

在动态分区分配方式中,系统为整个进程分配一个连续的内存空间。而在分段式存储管理系统中,则是为每个分段分配一个连续的分区,而进程中的各个段可以离散地移入内存中不同的分区中。为利用段表实现地址映射使程序能正常运行,亦即,能从物理内存中找出每个逻辑段所对应的位置,应像分页系统那样,在系统中为每个进程建立一张段映射表,简称“段表”。每个段在表中占有一个表项,其中记录了该段在内存中的起始地址(又称为“基址”)和段的长度。

利用段表实现地址映射

为了实现从进程的逻辑地址到物理地址的变换功能,由于段表一般都是存放在内存中的,在系统中设置了段表寄存器,用于存放段表基址和段表长度TL。

在进行地址变换时,系统将逻辑地址中的段号与段表长度TL进行比较。若S>TL,表示段号太大,是访问越界,于是产生越界中断信号;
若未越界,则根据段表的基址和该段的段号,计算出该段对应段表项的位置,从段表中能够中读出该段在内存的起始地址;
然后,再检查段内地址d是否超过该段的段长SL,若超过,即d > SL,同样发出越界中断信号;
若未越界,则将该段的基址d与段内地址相加,即可得到要访问的内存物理地址。

分段系统的地址变换过程

分段管理方式的提出则是考虑用户和程序员,以满足方便编程、信息保护和共享、动态增长及动态链接等多方面的需要。分段存储管理不会造成内部碎片化问题,但是同样有外部碎片化的问题。

分页式存储管理

用固定大小的页(Page)来描述逻辑地址空间,用相同大小的页框(Frame)来描述物理内存空间,由操作系统实现从逻辑页到物理页框的页面映射,同时负责对所有页的管理和进程运行的控制。

分页存储管理是将一个进程的逻辑地址空间分成若干个大小相等的片,称为页面或页,并为各页加以编号,从0开始,如第0页、第1页等。
相应地,也把内存空间分成与页面相同大小的若干个存储块,称为(物理)块或页框(frame),也加以编号,如0#块,1#块等等。
在为进程分配内存时,以页为单位将进程所需要的页面可以分别装入到多个不相邻的物理块中,提高计算机内内存的利用率。但是往往进程的最后一页所需要的实际内存空间并不满一个块,这就造成了“页内碎片”,也是内部碎片的一种。

在分页系统中的页面大小应适中。页面若太小,一方面虽然可使内存碎片减小,有利于提高内存利用率。但是另一方面也会使每个进程占用
较多的页面,从而导致进程的页表过长(页号->块号的映射关系表),占用大量内存,还会降低页面换进换出的效率。若页面较大,虽然可以减少页表的
长度,提高页面换进换出的速度,却又会使页内碎片增大。因此,页面的大小选择应适中,且页面大小应是2的幂,通常为512B~8KB。Linux和Windows x86系统都是默认页面大小为4k。

分页地址中的逻辑地址结构有两部分:前一部分是页号P,后一部分为页内位移量W(或称为页内地址)。
如下图所示:如果逻辑地址空间是2的m次方,页面大小是2的n次方(字节),那么逻辑地址的高m-n位是页号,低n位是页内偏移量。

分页地址结构

上述地址结构中,两部分的地址长度为32位,其中0~11位(计12位)为页内偏移量,即每页大小=2^12=4KB;12~31位(计20位)为页号,即
(页表项的个数)最多的页数=2^20=1MB。页内偏移量也称为页内位移、页偏移或页内地址等。对于某特定机器,其地址结构是一定的。

页表

页表是一个数据结构。我们知道对于一个进程,它的页面是可以和任何物理页框相对应的,所以我们在将逻辑地址转化为虚拟地址的时候就需要找到每个页面相对应的物理页框。这就是页表的作用。在进程地址空间内的所有页(0~n),依次在页表中有一个页表项,其中记录了相应页在内存中对应的物理块号。

页表的作用是实现从页号到物理块号的地址映射。由于内存页面和物理页框的大小是相同的,所以逻辑地址在页面内的偏移地址就是在对应物理页框中的偏移地址。可以说我们就可以得到逻辑地址对应的物理地址了。如下就是一个进程页表到物理块号的映射:

页表映射到物理块号

分页系统的地址转换

页表大多驻留在内存中。在系统中只设置一个页表寄存器PTR(Page-Table Register),在其中存放页表在内存的始址和页表的长度。平时,进程未执行时,页表的始址和页表长度存放在本进程的PCB中。当调度程序调度到某进程时,才将这两个数据装入页表寄存器中。因此,在单处理机环境下,虽然系统中可以运行多个进程,但只需一个页表寄存器。

当进程要访问某个逻辑地址中的数据时,分页地址变换机构会自动地将逻辑地址(相对地址)分为页号页内地址两部分,再以页号为索引去检索页表。
查找操作由硬件执行。在执行检索之前,先将页号与页表长度进行比较,如果页号大于或等于页表长度,则表示本次所访问的地址已超越进程的地址空间。于是,这一错误将被系统发现并产生一地址越界中断。
若未出现越界错误,则将页表始址与页号和页表项长度的乘积相加,便得到该表项在页表中的位置,于是可从中得到该页的物理块号,将之装入物理地址寄存器中。
与此同时,再将有效地址寄存器中的页内地址送入物理地址寄存器的块内地址字段中。这样便完成了从逻辑地址到物理地址的变换。下图为分页系统的地址变换机构:

分页系统的地址变换机构

多级页表

现代的大多数计算机系统,都支持非常大的逻辑地址空间(2^32~2^64)。在这样的环境下,页表就变得非常大,要占用相当大的内存空间。例如,对于一个具有32位逻辑地址空间的分页系统,如果使用简单的一级页表,如果进程使用全部4G线性地址空间,那么将需要高达2^20表项(总共地址线是32位,每页大小为4kb,则页偏移量需要低12位,高20位当作页表地址)来保存表示每个进程的页表,若每项4B,则需要4MB的ram来存储页表。而且还要求是连续的。这显然是比较困难的。
所以通常是通过多级页表来进行避免需要太多连续的内存,并且可以节省内存。所以这里这里主要说明一下为什么使用多级可以节省内存。

二级页表只是从进程的角度来看,为进程节省了页表项。对于进程而言,其往往并不会用到所有的虚拟地址空间,尤其是内核地址空间而言对于用户进程是不开放的,所以对于一级页表而言,我们就保存了许多根本不可能用到的页表项,其所对应的物理块号是无效的。但是由于查找的特性,对于单纯的一级页表而言,我们是不能删除这些页表项的。

对于二级页表而言,我们是先根据找到逻辑地址对应的一级页表,然后此时一级页表中存放的是二级页表的物理地址,然后我们再在对应的二级页表中查找真正的物理块号。从而得到物理地址。可以说一级页表中的每一个页表项对应的是一个比物理块更大的空间,真正的物理块号,我们从二级页表中查找得到。

因而,如果进程对于一个一级页表中的一个页表项中所代表的逻辑地址空间都没有使用,我们就不需要给其分配二级页表来保存相应的物理块映射。想一想,如果一个程序只用到了部分虚拟地址空间,就可以右一个一级页表,和几个二级页表就能够完成页表的映射了。

对于32位逻辑地址空间的分页系统,如果采用二级页表的方式。前10位表示一级页表中的页号,中间10位为二级页表的页号,最后10位同原来一样是页内/物理块内虚拟地址。

所以一级页表为2^10个表项,同样一个表项占用4B,就只需要4K的内存就可以完成一级页表,然后如果一级页表中相对应的页表表项是有效的,我们再给其创建二级页表,同样,一个一级页表的表项对应一个4K的二级页表。否则,二级页表就没有存在的必要。这就帮助我们节省了不少的内存,同时也减少了需要分配连续大内存所带来的麻烦。

两级页表的地址转换

页面置换功能

实际上,我们所探讨的分页存储管理还是很基本的,实际操作系统中的分页存储管理往往更复杂,但是大致思想都是相通的。这里再提另外一个概念,在分页存储管理方式中,如果不具备页面对换功能,则称为基本分页存储管理方式,或称为纯分页存储管理方式,它不具有支持实现虚拟存储器的功能,它要求把每个作业全部装入内存后方能运行。而具有页面兑换功能的分页存储管理,就可以先装入作业部分内容到内存中,在后续的进程执行中,如果所需要的页面并没有存储到当前的内存中,就会通过页面置换功能,将所需要的数据从磁盘中调入到内存中的一个页面上,同时将原来的内存页面调度出去。

现代的虚拟存储器基本上都是靠页面置换来可以保证每个进程都可以拥有一个独立的虚拟地址空间,而自己的物理空间不受其他进程的干扰。这方面的内容众多,可以参考《深入理解计算机系统》中的第九章 虚拟存储器来学习理解。这里就不再多说了。

段页式存储管理

分页系统能有效地提高内存的利用率,而分段系统能反映程序的逻辑结构,便于段的共享与保护,将分页与分段两种存储方式结合起来,就形成了段页式存储管理方式。
在段页式存储管理系统中,作业的地址空间首先被分成若干个逻辑分段,每段都有自己的段号,然后再将每段分成若干个大小相等的页。对于主存空间也分成大小相等的页,主存的分配以页为单位。
段页式系统中,作业的地址结构包含三部分的内容:段号 页号 页内位移量
程序员按照分段系统的地址结构将地址分为段号与段内位移量,地址变换机构将段内位移量分解为页号和页内位移量。
为实现段页式存储管理,系统应为每个进程设置一个段表,包括每段的段号,该段的页表始址和页表长度。每个段有自己的页表,记录段中的每一页的页号和存放在主存中的物理块号。

2.地址变换的过程:

  • 程序执行时,从PCB中取出段表始址和段表长度,装入段表寄存器。
  • 由地址变换机构将逻辑地址自动分成段号、页号和页内地址。
  • 将段号与段表长度进行比较,若段号大于或等于段表长度,则表示本次访问的地址已超越进程的地址空间,产生越界中断。
  • 将段表始址与段号和段表项长度的乘积相加,便得到该段表项在段表中的位置。
  • 取出段表项得到该段的页表始址和页表长度。
  • 将页号与页表长度进行比较,若页号大于或等于页表长度,则表示本次访问的地址已超越进程的地址空间,产生越界中断。
  • 将页表始址与页号和页表项长度的乘积相加,便得到该页表项在页表中的位置。
  • 取出页表项得到该页的物理块号。
  • 对该页的存取控制进行检查。
  • 将物理块号送入物理地址寄存器中,再将有效地址寄存器中的页内地址直接送入物理地址寄存器的块内地址字段中,拼接得到实际的物理地址。

段页式存储管理结合了段式存储管理和也是存储管理的优点,同时也保留了他们的缺点。段页式存储管理,内部碎片和外部碎片都有可能造成。