操作系统8-存储器管理

8 存储器管理1

  1. 根据冯· 诺依曼原理,程序必须先存储在内存中,才可以执行。
  2. 在多道程序并发执行的系统存储器管理非常重要。

8.1 程序的装入与链接

用户源程序执行通常要经过的步骤:

  • 1) 编译 由编译程序将用户源代码编译成若干个目标模块。
  • 2) 链接 由链接程序将编译后形成的目标模块以及它们所需要的库函数,链接在一起,形成一个装入模块。
  • 3) 装入 由装入程序将装入模块装入主存的过程。(加载)

操作系统8-存储器管理_第1张图片

8.1.1 程序的装入

把程序装入内存空间。采用三种方式:

  • 绝对装入方式(Absolute Loading Mode)
    在可执行文件中记录内存地址,装入时直接 定位在上述(即文件中记录的地址)内存地址。

    • 优点:装入过程简单。
    • 缺点:过于依赖于硬件结构,不适于多道程序 系统。
  • 可重定位方式(Relocatable Loading Mode)

    • 在可执行文件中,列出各个需要重定位的地址单元和相对地址值。
    • 当用户程序被装入内存时,一次性实现逻辑地址到物理地址的转换,以后不再转换。
      • 即装入时根据所定位的内存地址去修改每个重定位地址项,添加相应偏移量。
      • 一般在装入内存时由软件完成
    • 优点:不依赖于硬件,可以装入有限多道程序;无须硬件支持。
    • 缺点:一次性全部装入;一个程序通常需要占用连续的内存空间;程序装入内存后运行时不能移动。
  • 动态运行时装入方式(Dynamic Run-time Loading)

    • 在装入模块装入主存后,并不立即把装入模块中的相对地址转换为绝对地址, 而是把这种地址转换推迟到程序要真正执行时才进行。
    • 实现时需要重定位寄存器。
    • 优点:
      • 不需要一次装入;
      • OS可以将一个程序分散存放于不连续的内存空间,可以移动程序。
      • 能够支持程序执行中产生的地址引用,如指针变量。
    • 缺点:需要硬件支持,OS实现较复杂。它是虚拟存储的基础。
8.1.2 程序的链接
  • 链接程序的功能是将经过编译或汇编后所得到的一组目标模块以及它们所需要的库函数,装配成一个完整的装入模块。
  • 实现链接的方法有三种:
    • 静态链接:事先进行链接以后不再拆开的链接方式
    • 装入时动态链接:用户源程序经编译后所得到的目标模块,是在装入主存时,边装入边链接的。
    • 运行时动态链接:可将某些目标模块的链接,推迟到执行时才进行。

动态链接:
通常被链接的共享代码称为动态链接库 (DLL, Dynamic-Link Library)或共享库 (shared library)。

  • 优点:
    • 共享:多个进程可共用一个DLL,节省内存,减少文件交换。
    • 部分装入:进程可将多种操作分散在不同的DLL中实现,而只将当前操作相应的DLL装入内存。
    • 便于局部代码修改:即便于代码升级和代码重用;只要函数的接口参数(输入和输出)不变,则修改函数及其DLL,无需对可执行文件重新编译或链接。
    • 便于运行环境适应:调用不同的DLL,就可以适应多种使用环境和提供不同功能。如不同的显卡只需厂商为其提供特定的DLL,而OS和应用程序不必修改。
  • 缺点:
    • 链接开销:增加了程序执行时的链接开销;
    • 管理开销:程序由多个文件组成,增加管理复 杂度。

8.2 存储器管理的功能

8.2.1 主存分配

要完成内存的分配和回收工作,要求设计者选择和确定几种策略和结构:

  • 调入策略
    • 用户程序在何时调入内存的策略。
    • 目前有请调和预调两种。
  • 放置策略
    • 用户程序调入内存时,确定将其放置在何处的策略。
  • 置换策略
    • 当需要将某个用户程序调入内存而内存空间又不够时,就要确定哪个或哪些程序可以从内存中移走。
  • 分配结构
    • 用来登记内存使用情况的数据结构。
    • 如位示图、空闲分区表/队列等。

引起内存分配和回收的原因

  • 进程的创建和撤销
  • 进程运行过程中所占用的内存也可能发生变化,如栈的变化
  • 进程映像在内存和外存之间传递
    由于内存有限,系统中不可能容纳所有进程,有些进程的映像可以存放在外存,当要运行这些进程时必须把它们调入内存。
  • 为了充分利用内存空间,系统有时可能对内存空间进行调整
8.2.2 地址映射

把用户程序装入内存时对有关指令的地址部分的修改定义为从程序地址到内存地址的地址映射,或称为地址重定位。

  • 内存的每个存储单元都有一个编号,这种编号称为内存地址(或称为物理地址,绝对地址)。
  • 内存地址的集合称为内存空间(或物理地址空间)。
  • 要求用户用内存地址编程是非常困难的,尤其是在多道程序设计的环境中。
  • 用户编程所用的地址称为逻辑地址(或程序地址,或虚地址),由逻辑地址组成的空间称为逻辑地址空间(或程序地址空间)。
8.2.3 存储保护

保证在内存中的多道程序只能在给定的存储区域内活动并互不产生干扰。

  • 防止地址越界
  • 防止越权(对共享区有访问权)

实现方法:

  • 在CPU中设置一对下限寄存器和上限寄存器存放用户作业在主存中的下限和上限地址
  • 也可将一个寄存器作为基址寄存器,另一寄存器作为限长寄存器(指示存储区长度)
  • 每当CPU要访问主存,硬件自动将被访问的主存地址与界限寄存器的内容进行比较,以判断是否越界
  • 如果未越界,则按此地址访问主存,否则将产生程序中断——越界中断(存储保护中断)
8.2.4 主存扩充(虚拟内存)
  • 为了使程序员在编程时不受内存的结构和容量的限制,系统为用户构造一种存储器,其结构可能与内存结构不同,容量可能远远超过内存的实际容量。
  • 这种面向编程的存储器称为虚拟存储器。由虚存构成的存储空间称为虚存空间,或称虚地址空间。

实现虚拟内存的基本原理:

  • 由于程序在执行时,在一段时间内一般仅使用它的程序的一部分,所以程序仅有部分装入内存完全能够正确执行。
  • 将程序正在使用的部分内容放在内存,而暂时不用的部分放在外存,在需要时由系统调入内存,并将不需要(或暂不需要)的部分调出内存。
  • 由OS结合相关硬件来完成上述工作。

8.3 连续分配方式

连续分配方式是指为一个用户程序分配一个连续的内存空间。

8.3.1 单一连续分配
  • 最简单,适用于单用户、单任务的OS。
  • 内存分为两个区域:系统区、用户区。应用程序装入到用户区,可使用用户区全部空间。
  • 优点:易于管理。
  • 缺点:对要求内存空间少的程序,造成内存浪费;程序全部装入,很少使用的程序部分也占用内存。
8.3.2 固定分区分配
  • 管理思想
    • 把内存固定地划分为若干个固定大小区域
    • 每个分区装入一道作业
  • 分区的划分可采用分区大小相等和不等两种方式
  • 分区大小通常由计算机操作员或由操作系统给出,并维护分区说明表,包含每个分区的起始地址、大小及状态。
  • 当用户程序需装入内存时,检查分区说明表,如果有可用的满足要求的分区,则分配,并置该分区状态为“已分配”;否则不给该程序分配内存。

例:
某系统的内存容量为256K,操作系统占用低地址的20K,其余空间划分成4个固定大小的分区。
操作系统8-存储器管理_第2张图片

性能分析:

  • 在作业大小和出现频率均已知的情况下,固定分区是合适的。在这种情况下分区的大小选择与作业大小相当,这样内存的使用效率较高。
  • 但若作业的大小和出现的频率不知道时,势必造成分区的大小和作业的大小相差甚远,这样就会造成存储空间的浪费,从而影响整个系统的效率。
  • 优点:易于实现,开销小。
  • 缺点:
    • 内存碎片造成浪费
    • 分区总数固定,限制了并发执行的程序数目。
  • 可以和覆盖、交换技术配合使用。
8.3.3 动态分区分配

动态分区分配是指在系统运行的过程中建立分区,并使分区的大小刚好与作业的大小相等。

实现动态分区需要的数据结构:

  • 在动态分区存储管理中,要有相应的数据结构来登记空闲分区的说明信息,它包括空闲分区的大小和位置。
  • 不同系统根据设计要求采用不同的结构。常用的有空闲分区表和空闲分区队列结构。
  • 系统还要设置了等待分区队列,当系统中无空闲区或无满足要求的空闲区时,则把申请者送入等待队列中,等待别的进程释放内存之后再唤醒队列中的进程。

空闲分区表和空闲分区队列举例:
操作系统8-存储器管理_第3张图片

动态分区的分配和回收:

  • 分区的分配
    • 系统初启后,除操作系统占用一个分区外,其余存储区为一个大的空闲区。
    • 分区的分配是指系统根据用户的请求,在空闲分区表或空闲分区队列中寻找一个满足用户要求的空闲分区,把这个空闲分区分配给用户。
    • 以空闲分区表为例,当用户要求一个大小为SIZE的存储空间时,系统查询空闲分区表,找一个大于或等于SIZE的空闲区。
    • 分配时的三种情况
      • 一是系统中无满足要求的空闲分区,则分配失败。
      • 二是空闲区大小与SIZE相等,则修改空闲分区表相应表目,向用户返回该空闲分区首址,表示此空闲分区已分给了要求的用户。
      • 其三是空闲分区大于SIZE,这时将空闲分区一分为二。
        将一个空闲分区分成二部分有两种办法:
        • 从空闲分区的上部开始划出SIZE大小的空闲分区给用户;
        • 从空闲分区的底部开始向上划出SIZE大小的空闲分区给用户。
  • 分区的回收
    • 当某个进程释放某存储区时,系统首先检查释放区是否与系统中的空闲区相邻,若相邻则把释放区合并到相邻的空闲区中去,否则把释放区作为一个空闲区插入到空闲分区表的适当位置。
    • 释放区与空闲区相邻的四种情况
      操作系统8-存储器管理_第4张图片
      • 释放区与前空闲区相邻:将释放区与前空闲区合并为一个空闲区。其首址仍为前空闲区首址,大小为释放区大小与空闲区大小之和。
      • 释放区与前后两个空闲区相邻:将这三个区合为一个空闲区,其首址为前空闲区首址,大小为这三个区大小之和,并取消原后空闲区表目。
      • 释放区与后空闲区相邻:则把释放区合并到后空闲,首地址为释放区首地址,大小为二者大小之和。
      • 释放区不与任何空闲区相邻:将释放区作为一个空闲区,将其大小和首址插入到空闲区表的适当位置。

空闲分区表或队列的排序:

  • 按空闲区首址递增的次序归类组织空闲分区表或空闲分区队列;
  • 按空闲区大小的递增或递减次序组织空闲分区表或队列。

三种放置策略:
1.首次适应法(first-fit)

  • 要求空闲分区按首址递增的次序组织空闲分区表/队列
  • 分配:当进程申请大小为SIZE的内存时,系统从空闲区表的第一个表目开始查询,直到首次找到等于或大于SIZE的空闲区。从该区中划出大小为SIZE的分区分配给进程,余下的部分仍作为一个空闲区留在空闲区表中,但要修改其首址和大小。
  • 回收:按释放区的首址,查询空闲区表,若有与释放区相邻的空闲区,则合并到相邻的空闲区中,并修改该区的大小和首址,否则,把释放区作为一个空闲区,将其大小和首址按照首地址大小递增的顺序插入到空闲区表的适当位置。

首次适应法的优点:

  • 释放某一存储区时,若与空闲分区相邻则合并到相邻空闲分区中去,这种情况并不改变该分区在分区表中的位置,只要修改其大小或首址。
  • 这种算法是尽可能地利用低地址空间,从而保证高地址空间有较大的空闲区。

2.下次匹配法(next-fit) ——循环首次适应法
按空闲分区的前后次序,从上次分配的分区起查找(到最后分区时再回到开头),找到符合要求的第一个分区。

  • 该算法的分配和释放的时间性能较好,使空闲分区分布得更均匀,但较大的空闲分区不易保留。
  • 要求按空闲分区大小从小到大的次序组成空闲分区表/队列
  • 分配
    • 当进程申请一个存储区时,系统从表头开始查找,当找到第一个满足要求的空闲区时,停止查找,并且这个空闲区是最佳的空闲区。
    • 所谓最佳即选中的空闲区是满足要求的最小空闲区。
  • 回收:按释放区的首址,查询空闲区表(队列),若有与释放区相邻的空闲区,则合并到相邻的空闲区中,并修改该区的大小和首址,否则,把释放区作为一个空闲区插入空闲区表(队列)。
  • 分配和回收后要对空闲区表(队列)重新排序。

分析:
优点:

  • 在系统中若存在一个与申请分区大小相等的空闲区,必定会被选中,而首次适应法则不一定。
  • 若系统中不存在与申请分区大小相等的空闲区,则选中的空闲区是满足要求的最小空闲区,而不致于毁掉较大的空闲区。

缺点:

  • 空闲区的大小一般与申请分区大小不相等,因此将其一分为二,留下来的空闲区一般情况下是很小的,以致无法使用。随着时间的推移,系统中的小空闲区会越来越多,从而造成存储区的大量浪费。

3.最坏适应法(worst-fit)

  • 要求按空闲区大小递减的顺序组织空闲区表/队列。
  • 分配
    • 进程申请一个大小为SIZE的存储区时,总是检查空闲分区表的第一个空闲区的大小是否大于或等于SIZE。
    • 若空闲区小于SIZE,则分配失败;否则从空闲区中分配SIZE的存储区给用户,然后修改和调整空闲区表。
  • 回收
    • 按释放区的首址查询整个空闲分区表/队列
      • 若有与释放区相邻的空闲分区,则合并到相邻的空闲分区中,并修改该分区的大小和首址;
      • 否则把释放区作为一个空闲分区插入空闲分区表/队列。
  • 分配回收后要对空闲分区表/队列重新排序。

最坏适应法看起来似乎有些荒唐,但有它的优点:

  • 当程序装入内存中最大的空闲分区后,剩下的空闲分区还可能相当大,还能装下较大的程序;
  • 每次仅作一次查询工作。

三种策略比较;

  • 三种放置策略各有利弊,应针对具体作业序列来分析:
    • 对于某一作业序列,某种算法能将该作业序列中所有作业安置完毕,那么说该算法对这一作业序列是合适的。
    • 对于某一算法而言,如它不能立即满足某一要求,而其它算法却可以满足此要求,则这一算法对该作业序列是不合适的。

碎片问题:

  • 由于空闲区大小与申请内存的大小相等的情况是很少的,绝大多数情况是从一个空闲区中切去一块,剩下的部分作为一个空闲区仍留在空闲区表中,随着时间的推移,空闲区的发展趋势是越来越小,直至不能满足任何用户要求。
  • 这种不能被任何用户使用的极小的空闲区称为碎片。
  • 碎片的出现造成了存储空间的浪费。

在分区存储管理中解决碎片的办法

  • 规定门限值(由操作系统规定,如1K),分割空闲区时,若剩余部分小于门限值,则不再分割此空闲区。
  • 定期压缩存储空间,将所有空闲区集中到内存的一端,但这种方法的系统开销比较大。
  • 伙伴系统
8.3.4 可重定位分区分配

操作系统8-存储器管理_第5张图片

你可能感兴趣的:(操作系统)