处理器可以直接访问内存,但是不能直接访问外存。

只有通过I/O设备才可以将外存装入内存或者反过来。

但是固态硬盘即可直接访问,又可以掉电后保存信息但是写入速率与CPU不匹配,通常也还是作为外存使用。

第一节 概述

最理想的存储:高速、海量、非易失;实际上它们无法同时具有。因此,需要组合分层。

一、存储体系

目前最先进的存储部件仍明显慢于同级别CPU的速度;而且成本方面,任何一种存储部件都无法在速度与容量两方面同时满足用户需求。

【操作系统】第五章 存储管理_存储体系


二、存储管理任务

内存空间一般划分为:

  • 系统区:存放OS常驻内存的部分,用户不能使用。
  • 用户区:存储管理主要针对这部分。

内存管理包括:

  • 内存管理方法
  • 内存的分配与释放算法
  • 虚拟存储器管理
  • 控制内存和外存之间的数据流动方法
  • 地址变换技术
  • 内存数据保护与共享技术
1.内存的分配与回收

用时快速分配,不用时即使回收。

  • 记住每个内存区域的状态(已分配/空闲)
  • 实施分配
  • 静态
  • 动态
  • 回收

这些都通过内存分配表完成;它的组织方式有:

  • 位示图表示法:划分内存块,用1bit表示一个内存块的状态。
  • 链表法:将已分配的内存区域首地址和长度组成一条记录并将所有已分配的记录按照顺序组成链表,形成已分配内存链表。同样地形成空闲内存链表。

分配方式:

  • 静态:分配工作在程序运行前一次性低完成且不再分配。
  • 动态:分配工作在程序运行前即允许中逐步完成。
2.内存共享

两个或多个进程共享内存中相同的区域。

目的:

  • 代码共享,节省空间;提高内存利用率。(必须是纯代码)
  • 数据共享,实现进程通信。
3.内存保护

避免内存中个程序互相干扰,各程序只能访问自己的区域;这通常需要硬件的支持并由软件配合实现。

内容:

  • 保护系统程序区不被侵犯
  • 不允许用户程序读写不属于自己的程序空间

方法:(硬件为主,软件为辅)

  • 地址越界保护
  • 对程序产生的地址必须加以检查,发生越界则产生中断,由OS进行处理。
  • 权限保护
  • 针对共享区域
  • 自己的区域:可读可写
  • 以获得授权的区域:可读不可写
  • 未获得授权的区域:不可读不可写
4."扩充"内存容量

由于程序运行时受物理限制;OS使用户得到比物理内存容量大得多的内存空间。

硬件支持下,软硬件协作,将内存与外存结合的方法。

三、地址转换

一般而言,存储器以字节(8个二进制位)为编址单位;每个字节都有一个地址与其对应。

假设某存储器容量为n字节,其地址编号顺序为:0,1,2,...,n-1。这些地址被称为内存的"绝对地址",与之对应的内存空间称为"物理地址空间"。

多道程序设计的系统中,内存中同时存放了多个应用程序。OS根据内存的使用情况为用户分配内存空间;用户是不能预知其程序将被存放到内存的什么位置的。所以,用户程序中就不能使用据对地址。

因此,为了方便,每个用户都可以认为自己的程序和数据存放在一块以0地址开始的连续空间中;这种地址就是逻辑地址,与其对应的内存空间叫做"逻辑地址空间"。

地址重定位

当用户程序请求执行时,OS的内存管理要为其分配合适的内存空间,这个分配的内存空间可能是从某物理内存单元开始的一块连续的内存空间。

例如:某程序被装入内存地址为A的来连续区域,当程序中指定访问k单元时,实际应访问内存的A+k单元。

CPU必须按实际位置(绝对地址)去访问内存。

将逻辑地址转换为绝对地址的过程称为"地址重定位"/"地址转换"/"地址映射"。

方法可分为:

  • 静态重定位:在程序开始执行前将逻辑地址全部转换为物理地址。
  • 动态重定位:程序装入内存时不转换,在执行时,每执行一条指令都由硬件的地址转换机构转换为绝对地址。

静态重定位:

【操作系统】第五章 存储管理_存储体系_02

动态重定位:

【操作系统】第五章 存储管理_分区管理_03

地址转换机构由一个基址寄存器和一个地址加法器构成。

动态重定位的情况下,可以改变程序在内存中的位置。

第二节 分区管理方案

分区管理方案时满足多道程序运行最简单的存储方案。

思想:内存划分成若干连续区域(分区);每个分区装入一个程序。

一、固定分区

1.思想

将内存分为若干大小固定,大小可以不同的分区;划分好之后,系统运行期间不再重新划分。

每个分区划分好之后大小固定;因此可容纳的程序的大小就有限制;程序也必须提供对内存资源的最大申请量,当系统中有满足条件的分区才将程序装入。

2.内存分配表与分区的分配回收

每个分区在内存分配表对应一个表项;每个表项包含:

  • 分区号(区号)
  • 分区大小
  • 分区起始地址
  • 使用状态(空闲/已占用)

固定分区:不能充分利用内存,因为一个程序的大小不可能刚好等于某个分区大小;易产生碎片。


二、可变分区

1.思想

系统不预先划分分区,而是在装入时划分,使程序分配的分区大小正好等于程序的需求量。

OS启动后,内存中除了OS占用的分区外,其余空间为一个完整的大空闲区。

2.内存紧缩

内存经过一段时间的使用后,会存在很多很小的空闲块(碎片)。

解决办法:在适当的时进行碎片整理,即移动内存中的进程,将内存中空闲的碎片合并成一个连续的大的空闲空间,这个过程就是"内存紧缩"/"紧缩技术"/"压缩技术"。

问题:

  • 会增加系统开销
  • 移动是有条件的;正在与外部设备交换信息的进程不能移动。
3.可变分区的实现

需要有硬件的地址转换机构的支持。

两个专用寄存器:

  • 基址寄存器:存放程序所在分区的起始地址。
  • 限长寄存器:存放程序所在分区的长度。

程序装入进程后称为进程

【操作系统】第五章 存储管理_夏明亮_04

程序所在分区的起始地址和分区的长度会存入PCB,在进程被调度后,它们作为现场信息送入对应的寄存器中。

使用已分配内存链表空闲内存链表来管理可变分区。

【操作系统】第五章 存储管理_存储体系_05


4.空闲区的分配策略(算法)
  • 最先适应算法(顺序分配算法):快

【操作系统】第五章 存储管理_虚拟存储_06

  • 最优适应算法:尽量不分割大的空闲区域

【操作系统】第五章 存储管理_存储体系_07

  • 最坏适应算法:优先分割大的空闲区域,后续遇到大程序可能无法满足

【操作系统】第五章 存储管理_分区管理_08


5.分区的回收

进程执行完成后执行回收。

将其记录在空闲分区表中;回收时,应首先检查相邻(上邻、下邻)分区是否空闲;若空闲则合并

三、分区管理方案的优缺点

优点

  • 使内存成为共享资源;
  • 外设与CPU的利用率提高;
  • 多道程序设计实现;
  • 管理简单、开销小、保护措施简单。

缺点

  • 内存使用不充分,碎片严重;
  • 移动碎片开销大;
  • 不能为用户提供"虚存";
  • 每个用户程序的存储仍受物理内存容量限制;
  • 要求程序一次性全部装入内存后才能运行。

第三节 覆盖和交换技术

无论内存多大,程序所需都可能更大。

将程序的一部分放在外存;而将那些当前需要执行的程序段和数据段放入内存。解决程序大于物理内存的问题。

即:OverLay技术(早期)和Swapping技术(目前主要用于小型分时OS)。

一、覆盖技术

一个程序的若干程序段共享某一个存储空间。

将程序划分为若干个功能上相对独立的程序段;使那些不会同时执行的程序段共享同一个块内存区域。

未执行的程序段线保存在磁盘上,当有关程序段前一部分执行完成后,把后续程序段调入内存,覆盖前面的程序段。

Overlay无需OS的特殊支持,它是用户程序自己附加控制;需要程序员提供清晰的覆盖结构,即程序员需要把一个程序划分成不同的程序段,并规定哈他们的执行和覆盖顺序。

他对用户不透明,增加了用户负担;对程序段最大长度仍受限于物理内存容量。

覆盖技术主要用于单个程序内部的内存管理,以解决单个程序太大而无法全部装入内存的问题。这种技术在现代操作系统中很少使用,因为现代操作系统通常使用虚拟内存管理技术来处理大程序的内存需求。

二、交换技术

进程从内存移到磁盘,并再移回内存称为交换;这是由OS控制的,多用于分时OS,大多数现代OS都使用这种技术;交换技术也是虚拟存储的基础。

1.换出进程的选择

一般用时间片轮转调度算法或基于优先级的调度算法选择调出;调出进程是短时间内不会投入运行的程序。

2.交换时机的缺点

一般是内存不足或存在内存不足危险时换出;或者发现长时间没有运行的进程。

3.交换空间的分配

方法1:进程一旦建立即为其分配交换空间。

方法2:被换出时分配空间,每次交换可能被换到不同的地方。

4.换入进程换回内存时位置确定

不一定会回到原位置;

  • 若进程中引用绝对地址,则必须换回原位。
  • 若进程中引用相对地址,则可以换回到非原位。

交换技术的缺点:

花费大量CPU时间,所以关键应减少交换的信息量;合理的做法是再外存中保留每个程序交换副本,换出时仅交换修改过的部分。

交换技术的优点:

打破了一个程序一旦进入内存就要一直运行到结束的限制;对用户透明;交换可发生在不同进程或程序之间;覆盖发生在同一进程或程序内部;因此,交换技术应用更加广泛。

第四节 虚拟页式存储管理方案

这一节和《计算机系统结构中》的存储体系有多类似;可以参考我之前的系列文章:

https://blog.51cto.com/mlxia/10251375

https://blog.51cto.com/mlxia/10346165

分区方式管理内存时;每道程序均占用一个或连续几个存储空间;因此,内存无足够大连续空间时,程序时无法装入的。

早期方案(如:覆盖):先由程序员指定后由OS辅助完成,但对程序员要求太高,易出错;根本方法应该是将此类工作全部交给OS。

因此,产生了虚拟存储技术(本质是部分装入);可以将逻辑地址连续的程序分散存放到不连续的内存中,并保证程序的正确执行;即可充分利用内存又可以减少移动开销。

大多数的OS都采用虚拟页式存储管理方案。

一、虚拟存储技术

思想:利用大量外存来扩充内存,产生一个比实际内存大得多的、逻辑的虚拟存储空间,简称"虚存"。

虚拟存储管理是由OS在硬件支持下对两级存储器(内存和外存)实施统一管理,OS将当前使用的部分保留在内存中,其他部分保存在磁盘上,并在需要的时候动态交换。

虚拟存储器的容量也是有限制的,主要受:

  • 地址空间字长
  • 外存容量

的限制。

内存地址空间

内存地址空间指的是一个计算机系统能够直接访问的内存范围。它由地址总线的宽度决定,地址总线的每一条线都可以表示一个比特位。地址总线的宽度通常用位(bits)来表示。

字长(Word Size)

字长是计算机系统处理数据的基本单位,通常以比特位(bits)为单位。字长决定了处理器一次可以处理的最大数据位数。

内存地址空间的字长

内存地址空间的字长指的是用于表示内存地址的位数。它决定了系统可以寻址的最大内存范围。内存地址空间的字长通常与计算机的地址总线宽度相同。

理解内存地址空间字长

  1. 地址空间的计算

内存地址空间的大小可以通过以下公式计算:

【操作系统】第五章 存储管理_虚拟存储_09

例如,如果地址总线的宽度为 32 位,则内存地址空间为:

【操作系统】第五章 存储管理_虚拟存储_10

  1. 字长与地址空间的关系
  • 32 位系统:在一个32位系统中,地址总线和字长通常都是32位。这意味着该系统可以寻址的最大内存空间是4GB。
  • 64 位系统:在一个64位系统中,地址总线和字长通常都是64位。这意味着该系统可以寻址的最大内存空间是巨大的,理论上可达 【操作系统】第五章 存储管理_虚拟存储_11 字节(16 EB,即 exabytes)。

实现虚拟存储器需要以下硬件支持:

  • 系统有足够大的外存
  • 系统有一定容量的内存
  • 最主要的:硬件提供实现虚<->实地址映射机制

虚拟存储器与交换技术在原理上是类似的; 区别在于:

  • 交换技术:以进程为单位。
  • 虚拟页式存储方案:以页为单位。

二、虚拟页式的存储管理

支持页式存储管理的硬件部件称为存储管理部件(Memory Management Unit,MMU);它将内存分为大小相等的块,每个块称为"物理页面"或"页框";同时,要求程序中的逻辑地址也以同样的大小分页。

【操作系统】第五章 存储管理_虚拟存储_12

三、页式存储管理中物理内存的分配与回收

以物理页面位单位进行;简单的内存分配表可以用一张"位示图"表示。

位图(Bitmap)结构

位图是一个数组,其中每一位(bit)代表一个固定大小的内存块的使用情况:

  • 0 表示该内存块是空闲的,可以分配。
  • 1 表示该内存块已经被分配,不可用。

位图的长度取决于内存的总大小和内存块的大小。例如,如果内存总大小为1GB,内存块大小为4KB,则需要 【操作系统】第五章 存储管理_存储体系_13,即 【操作系统】第五章 存储管理_页面替换算法_14的位图。

位图的结构示例

假设我们有一块 32KB 的内存,内存块大小为 4KB,则需要一个 8 位的位图:

  • 位置 0:块 0 的状态
  • 位置 1:块 1 的状态
  • 位置 2:块 2 的状态
  • 位置 3:块 3 的状态
  • 位置 4:块 4 的状态
  • 位置 5:块 5 的状态
  • 位置 6:块 6 的状态
  • 位置 7:块 7 的状态

初始位图:00000000(表示所有内存块都是空闲的)。

位图的使用
  1. 分配内存
  • 操作系统扫描位图,寻找第一个连续的空闲块(位为0),找到后,将对应位设置为1,表示这些块已被分配。
  • 例如,要分配一个 8KB 的内存块(需要两个 4KB 的块),扫描位图 00000000,找到前两个空闲块,更新位图为 11000000
  1. 释放内存
  • 操作系统将释放的内存块对应的位设置为0,表示这些块重新变为可用状态。
  • 例如,释放上面分配的 8KB 内存块,更新位图为 00000000

示例操作

假设我们有一个 32KB 的内存区域,块大小是4KB,初始位图为 00000000

  • 分配 8KB 内存
  • 查找两个连续的空闲块:位置0和位置1。
  • 更新位图为 11000000
  • 分配 4KB 内存
  • 查找一个空闲块:位置2。
  • 更新位图为 11100000
  • 释放第一个 8KB 内存块
  • 更新位图为 00100000
  • 分配 16KB 内存
  • 查找四个连续的空闲块:位置3、4、5、6。
  • 更新位图为 00111110

位图的优缺点

优点:

  • 简单高效:位图的结构和操作都非常简单,只需基本的位运算和扫描操作。
  • 紧凑:位图占用的内存非常小,相对于其他复杂的数据结构(如链表或树)更加紧凑。

缺点:

  • 连续内存分配困难:在高度碎片化的内存环境中,寻找连续的空闲块可能会变得困难和低效。
  • 扫描开销:随着内存大小的增加,扫描位图找到空闲块的开销也会增加。

四、虚拟页式存储地址转换过程

需要有硬件的地址转换机构支持。

1、页式存储管理的地址转换

需要的页表控制寄存器有:页表始址寄存器和页表长度寄存器;再加上高速缓存存储器。

当进程生成时产生PCB,当进程被调度选中时,从PCB中取出该进程的页表在内存中的其实地址放入虚拟页式存储地址转换过程,将页表长度放入页表长度寄存器。

每个程序的页表长度不同,页表时地址转换的依据。

物理地址 = 物理页面号 x 块长 + 页内地址。

物理页面号又叫页帧号或者页框号。

虚拟页式存储的基本概念

  1. 虚拟地址:由程序生成的地址,假设是一个32位的地址空间。
  2. 物理地址:内存中实际的地址。
  3. 页(大小):虚拟地址空间和物理地址空间都划分成固定大小的块,通常为4KB。
  4. 页表:存储虚拟页到物理页的映射关系。

地址转换过程

地址转换过程包括以下几个步骤:

  1. 虚拟地址分解:虚拟地址被分为页号(Page Number)和页内偏移量(Offset)。
  2. 页表查找:通过页号在页表中找到对应的物理页号。
  3. 物理地址计算:将物理页号和页内偏移量组合,形成物理地址。

示例

假设我们有一个32位虚拟地址,页面大小为4KB(【操作系统】第五章 存储管理_分区管理_15字节),即页内偏移量为12位(页面大小的指数一致)。虚拟地址的结构如下:

虚拟地址 = 页号(20位) + 页内偏移量(12位)

假设一个进程生成了虚拟地址 0x12345678,我们需要将其转换为物理地址。

步骤1:虚拟地址分解

将虚拟地址 0x12345678 转换为二进制:(十六进制转二进制)

0x12345678 = 0001 0010 0011 0100 0101 0110 0111 1000

根据页面大小为4KB(12位偏移量),我们可以分解出:

  • 页号(Page Number):0001 0010 0011 0100 0101(共20位)
  • 页内偏移量(Offset):0110 0111 1000(共12位)

步骤2:页表查找

假设页表如下(每个条目占用4字节):

虚拟页号

物理页号

0x12345

0x1A2B3

通过查找页表,虚拟页号 0x12345 映射到物理页号 0x1A2B3

步骤3:物理地址计算

将物理页号 0x1A2B3 和页内偏移量 0110 0111 1000 组合:

  • 物理页号 0x1A2B3 = 0001 1010 0010 1011 0011 # 十六进制转二进制
  • 页内偏移量 0110 0111 1000

组合后得到物理地址:

物理地址 = 0001 1010 0010 1011 0011 0110 0111 1000
          = 0x1A2B3678

虚拟页式存储通过将虚拟地址分解为页号和页内偏移量,利用页表查找对应的物理页号,再组合成物理地址,实现了地址转换。这种方式有效地解决了内存管理中的碎片化问题,同时为程序提供了一个连续的虚拟地址空间。

2、页表项

进程运行前装入一部分页面而不是全部装入,之后根据需要动态装入,内存满时淘汰某个页面后再装入新页面。

页表项(Page Table Entry, PTE)包含了虚拟页和物理页之间映射关系的具体信息。不同的操作系统和处理器架构可能会有不同的页表项结构,但通常会包含以下几类信息:

  • 物理页框号(Physical Frame Number, PFN):对应的物理内存中的页框号。
  • 有效位(Valid Bit):表示该页是否在物理内存中。
  • 修改位(Dirty Bit):表示该页是否被写入过。
  • 访问位(Access Bit):表示该页是否被访问过。
  • 保护位(Protection Bits):定义该页的访问权限,如只读、读写、执行等。
  • 其他控制和状态位:如缓存禁止位(Cache Disable Bit)、全局位(Global Bit)等。

页表项中没有虚拟页面号,是因为页表项本身只是用来描述虚拟页面号和物理页面号的映射关系。在实际的页表结构中,虚拟页面号是通过访问页表的位置(索引)确定的,而不是存储在页表项中。

使用虚拟页号(例如前面例子中的 0x12345虚拟页面号) 作为索引在页表中查找对应的页表项。

示例

假设一个32位系统,页面大小为4KB,每个页表项大小为4字节,页表项结构如下:

31                      12 11 10 9  8  7  6  5   4  3  2  1  0
+------------------------------------------------------------+
| 物理页框号(PFN)        |   D |  A |   P |  U |  R |  C |  V |
+------------------------------------------------------------+
  • 物理页框号(PFN):20位,用于指向物理内存中的页框。
  • D(Dirty Bit):1位,表示该页是否被写入过。
  • A(Access Bit):1位,表示该页是否被访问过。
  • P(Protection Bits):2位,定义页的访问权限。
  • U(User Bit):1位,表示该页是否为用户模式可访问。
  • R(Reserved):1位,保留位。
  • C(Cache Disable Bit):1位,表示是否禁用缓存。
  • V(Valid Bit):1位,表示该页是否有效。

假设我们有如下页表项:

0x0ABCDE31

将其转换为二进制:

0x0ABCDE31 = 0000 1010 1011 1100 1101 1110 0011 0001

根据上述结构,我们可以分解出各字段:

  • 物理页框号(PFN)0000 1010 1011 1100 1101(20位) = 0xABCDE,表示该页在物理内存中的页框号。
  • D(Dirty Bit)1(第11位),表示该页被写入过。
  • A(Access Bit)1(第10位),表示该页被访问过。
  • P(Protection Bits)00(第9和第8位),表示该页的访问权限为只读。
  • U(User Bit)1(第7位),表示该页可由用户模式访问。
  • R(Reserved)0(第6位),保留位,无具体含义。
  • C(Cache Disable Bit)0(第5位),表示允许缓存。
  • V(Valid Bit)1(第0位),表示该页有效。
3、页表
1. 单级页表

概念

单级页表是最简单的页表结构,直接用一个大的数组来存储虚拟页号和物理页号的映射关系。

使用方法

假设一个系统有32位的虚拟地址空间,每个页面大小为4KB(12位偏移量),则虚拟页号为20位。如果我们有一个包含1M个页表项(2202^{20}220),每个页表项占用4字节,总共需要4MB的内存来存储页表。

示例

  • 虚拟地址:0x12345678
  • 虚拟页号:0x12345
  • 页内偏移量:0x678
  • 页表项:假设页表中虚拟页号 0x12345 映射到物理页号 0xABCDE

查找过程:

  1. 提取虚拟页号 0x12345
  2. 查找页表,找到物理页号 0xABCDE
  3. 计算物理地址:0xABCDE678
2. 多级页表

概念

大多数现代计算机都支持很大的地址空间;因此,使得页表本身会很大。例如,支持32位虚地址空间时,若页面大小是4KB,则页表【操作系统】第五章 存储管理_页面替换算法_16个表项;假设没和表项有4个字节组成,那么页表就是4MB。

多级页表将页表划分为多个级别,以减少内存开销。每一级页表都指向下一级页表,最后一级页表存储实际的物理页号。

例如:分两级的情况下,页面大小4KB的32位计算机;虚拟地址划分为10位页目录+10位页表+12位页内偏移。

使用方法

假设一个系统有32位的虚拟地址空间,每个页面大小为4KB(12位偏移量),使用两级页表。第一页表的每个条目指向一个二级页表。

示例

  • 虚拟地址:0x12345678 = 0001 0010 0011 0100 0101 0110 0111 1000
  • 虚拟地址分解:[10位 一级页号][10位 二级页号][12位 页内偏移量]
  • 一级页号:0x48 // 0001 0010 00 -> 0000 0100 1000 -> 0x48
  • 二级页号:0x345 // 11 0100 0101 -> 0011 0100 0101 -> 0x345
  • 页内偏移量:0x678

查找过程:

  1. 提取一级页号 0x48
  2. 查找一级页表,找到二级页表的基地址
  3. 提取二级页号 0x345
  4. 查找二级页表,找到物理页号 0xABCDE
  5. 计算物理地址:0xABCDE678
3. 散列页表

概念

散列页表使用哈希函数将虚拟页号映射到哈希表的索引。解决冲突时,可以使用链表等结构。

当地址空间大于32位时,常见做法是以页号位散列值的散列页表,其中每个表项都包含一个链表。

使用方法

假设我们使用一个哈希函数 hash(vpn) = vpn % N,其中 vpn 是虚拟页号,N 是哈希表的大小。

示例

  • 虚拟地址:0x12345678
  • 虚拟页号:0x12345
  • 页内偏移量:0x678
  • 哈希函数:hash(0x12345) = 0x12345 % N

查找过程:

  1. 计算哈希值:hash(0x12345) = h
  2. 查找哈希表,找到链表头指针
  3. 遍历链表,找到虚拟页号 0x12345 对应的物理页号 0xABCDE
  4. 计算物理地址:0xABCDE678
4. 反置页表

概念

反置页表将物理内存中的每一页与虚拟页面的映射关系存储在一个表中,而不是为每个进程维护一个独立的页表每个物理页框都有一个条目,指示哪个进程的哪个虚拟页映射到该物理页框

缺点是:增加了访问内存时的查表时间 。

使用方法

假设一个系统有32位的虚拟地址空间,页面大小为4KB,每个物理页框在反置页表中都有一个条目,包含进程ID和虚拟页号。

示例

  • 虚拟地址:0x12345678
  • 虚拟页号:0x12345
  • 页内偏移量:0x678

查找过程:

  1. 使用反置页表中的线性搜索或哈希查找,找到包含进程ID和虚拟页号 0x12345 的条目
  2. 找到物理页框号 0xABCDE
  3. 计算物理地址:0xABCDE678
4、转换检测缓冲区(Translation Lookaside Buffer:TLB或快表)

一般来说页表存放在内存中,当按虚拟地址访问时,必须访问内存两次:第一次按页号读取页表中的页框等信息;第二次按计算出的绝对内存地址i进行读写。

想要加速 ,有办法:

  • 方法1:地址映射机制中增加一组高速缓冲寄存器用户保存页表;此方案经济上不可行。
  • 方法2:地址映射机制中增加一个小容量的联想寄存器(相联存储器);利用高速缓冲存储器存放最频繁【根据存储访问局部性原理】被访问的少数页面;该高速缓冲存储器也叫快表。
5、缺页异常处理

要访问的页面不在内存中则会产生缺页异常。

修改过的页面,要先写回磁盘,然后再被替换。

6、页面调度策略

这些策略决定了页面在内存和磁盘之间的移动,优化内存使用,提高系统性能。

(1)调入策略

决定了何时将页面从磁盘调入内存。有两种主要的调入策略:

  1. 请求调页(Demand Paging)
  • 只有当一个页面被访问而且不在内存中时,才将其调入内存。
  • 优点:减少不必要的内存使用。
  • 缺点:首次访问页面时会有显著的延迟(页面错误处理开销)。

示例: 当进程试图访问一个尚未加载的页面时,会触发一个页面错误,操作系统将页面从磁盘调入内存。

  1. 预调页(Prepaging)
  • 在实际需要之前,将页面预先加载到内存中。
  • 优点:减少未来的页面错误,适用于顺序访问模式。
  • 缺点:可能会调入不必要的页面,增加内存开销。

示例: 如果程序经常顺序访问数据,操作系统可以提前加载接下来的几个页面。

(2)置页策略

置页策略(Page Placement Policy)决定了页面被放置到内存中的位置。主要考虑的是如何将页面放置在物理内存的不同位置。

和本章节前面:空闲区的分配策略是一样的。

  1. 首次适应(First Fit)
  • 将页面放入第一个足够大的空闲块。
  • 优点:简单,快速。
  • 缺点:可能导致内存碎片化。
  1. 最佳适应(Best Fit)
  • 将页面放入最小的、适合的空闲块。
  • 优点:减少大块空闲内存的浪费。
  • 缺点:可能会增加内存碎片化。
  1. 最差适应(Worst Fit)
  • 将页面放入最大的空闲块。
  • 优点:避免生成过多的小碎片。
  • 缺点:可能会浪费大块内存。
(3)置换策略

置换策略(Page Replacement Policy)决定了当内存已满且需要调入新页面时,哪些页面需要被替换出去。

1.固定分配局部置换(FALR)

每个进程分配固定的内存空间,且整个运行过程期间不再改变;若缺页,则从分配的空间中换出一页、换入一页。

2.可变分配全局置换(VAGR)

每个进程分配一定量的内存空间;OS本身保持一个空闲物理页面队列;缺页时,从空闲队列分配,当空闲队列用完才置换一页,这一页可能是任意进程的一页。

3.可变分配局部置换(VALR)

每个进程分配一定量的内存空间;缺页时只从自己进程的页中选择置换的页;若检测到进程频繁缺页,则OS再增加分配若干物理页面,直到缺页率降至某个合理的范围。

7、页面替换算法

每当缺页时随便选择一个页面换出虽然可以,但是有可能被换出的页面很快又要被用到,从而需要重新将其换入;这样就增加了系统的开销。

应用合理的算法规避着这种情况可以提高系统的性能。

这种平凡装入、换出的现象被称为"抖动"或"颠簸"。

以下是几种常用的页面替换算法:

(1)理想页面置换(OPT)

  • 替换将来不再使用或以后会使用的页面中以后最长时间才会再次使用的页面。【每次页面替换时,标记将被替换的页面,并在下一次缺页时进行替换。】
  • 当页面已经在内存中时,不执行任何操作,页面保留在内存中。
  • 理想情况下的最佳性能,但实际实现难度大,因为需要预知未来的页面访问情况。
  • 它可以作为一个衡量其他页面淘汰算法的标准

(2)先进先出(FIFO,First-In-First-Out):

  • 最早进入内存的页面最先被置换出去。
  • 当页面已经在内存中时,不执行任何操作,页面仍然按其进入内存的顺序保留在队列中。不会因为新出现已经在内存这种的页面重新排队。
  • 优点:简单实现。
  • 缺点:不考虑页面使用频率,可能替换掉经常使用的页面。

示例: 一个页面进入内存时放入队列,队列头的页面最先被替换。

(3)第二次机会页面置换算法(Second Chance)

  • 它是对FIFO算法的改进。
  • 检查进入内存时间最久页面页表的R位(访问位),如果是0,则该页面既老又没被使用过,可以立即换掉;如果是1,那么将R置为0,并将该页面放到链表尾端,并修改起进入内存的时间,然后继续搜索,直到找到一个最近的时钟间隔以来从来没有被访问过的页面。
  • 如果所有页面都被访问过,该算法就退化为了FIFO。
  • 缺点:经常需要在链表中移动页面,既低效又不是很有必要。

(4)时钟页面置换算法(Clock)

  • 采用环形队列和一个指针(时钟手)来选择置换页面,结合FIFO和LRU。
  • 将所有页面保存在一个类似时钟面的链表中,一个表针指向最老的页面;当缺页时,首先检查表针指向的页面,若R=0就将其换出,并将表针前移一个位置;若R=1,则清除R并将表针前移一个位置,直到找到R=0的页面。
  • 优点:比LRU简单,性能接近。
  • 缺点:可能仍然替换掉频繁使用的页面。

(5)最近最少使用(LRU,Least Recently Used):

  • 原理:通常之前被频繁使用的页面,之后也会被使用;之前很久不使用的页面,后续很可能也不会使用。
  • 缺页时,最近最少使用(最长时间未被使用)的页面最先被置换。
  • 需要页表中为每一页增加一个计时标志(上次被访问以来所经历的时间),每次被访问后都置为0;当执行一次置换后,将所有页的计时标记都置为0。
  • 优点:考虑页面使用历史,结果最接近OPT。
  • 缺点:实现复杂,维护开销大。

示例: 操作系统通过硬件或软件维护每个页面的访问时间戳,选择时间戳最小的页面置换。

**例子:**某程序在内存中分到3个页面,初始值置为空,所需页面走向为:4、3、2、1、4、3、5、4、3、2、1、5;请分别使用OPT、FIFO和LRU算法描述每时刻内存中页面状态以及缺页和命中情况。

OPT算法:

页面流

4

3

2

1

4

3

5

4

3

2

1

5

物理页面1

4

4

4

4

4

4

4

4

4

2

1

1

物理页面2


3

3

3

3

3

3

3

3

3

3

3

物理页面3



2

1

1

1

5

5

5

5

5

5

Hit





H

H


H

H



H

我这里与书上不同,但缺页结果一样都是7;我这里应该更准确。

FIFO算法:

页面流

4

3

2

1

4

3

5

4

3

2

1

5

物理页面1

4

4

4

1

1

1

5

5

5

5

5

5

物理页面2


3

3

3

4

4

4

4

4

2

2

2

物理页面3



2

2

2

3

3

3

3

3

1

1

Hit








H

H



H

我这里与书上不同,但缺页结果一样都是9;我这里应该更准确。

LRU算法:

页面流

4

3

2

1

4

3

5

4

3

2

1

5

物理页面1

4

4

4*

1

1

1*

5

5

5*

2

2

2

物理页面2


3

3

3*

4

4

4*

4

4

4*

1

1

物理页面3



2

2

2*

3

3

3*

3

3

3*

5

Hit








H

H




我这里与书上不同,但缺页结果一样都是10;我这里应该更准确。

8、缺页率

与谁有关?

  • 分给进程的物理页面数量
  • 页面大小;页面大存储的信息多
  • 程序的编制方法
  • 页面置换算法性能

计算公式:

【操作系统】第五章 存储管理_存储体系_17

五、虚拟页式存储管理的优缺点

优点:不要求连续的内存空间,解决了碎片问题;提高了内存的利用率;有利于组织多道程序的运行。

缺点:存在页面浪费问题,最后一页总会出现浪费;页面越大可能浪费的更多。

六、虚拟存储管理的性能问题

目的:减少内外存交换次数。

抖动/颠簸时,用于调度页面的时间可能比进程真正用于执行所占用的时间还要多。

抖动/颠簸时由于缺页率高引起的。

可以采用工作集模式来解决这个问题。

工作集模式通过动态监测进程的实际需求页面集合,来决定哪些页面应当留在内存中,哪些页面可以换出,从而提高系统的性能。

工作集是指在某一特定时间段内,进程正在使用的所有页面的集合。这个时间段称为“工作集窗口”(Working Set Window),通常用页面访问的次数来表示,而不是绝对时间。

工作集算法的原理

  1. 监测页面访问:操作系统监测进程的页面访问情况,记录每个页面的最后访问时间。
  2. 确定工作集窗口:为每个进程设定一个工作集窗口(Δ\DeltaΔ),表示在过去的Δ\DeltaΔ次页面访问中,进程的工作集包含哪些页面。
  3. 维护工作集:在工作集窗口内的页面被认为是进程当前需要的页面,应该留在内存中;超出工作集窗口的页面可以被置换出去。

示例

假设系统的工作集窗口Δ大小为 5 次页面访问,页面访问序列为:A, B, C, A, B, D, A, E, F, A, B, C, D

我们逐步分析工作集的变化:

  1. 访问 A:工作集 = {A}
  2. 访问 B:工作集 = {A, B}
  3. 访问 C:工作集 = {A, B, C}
  4. 访问 A:工作集 = {A, B, C}(A 已在工作集中)
  5. 访问 B:工作集 = {A, B, C}(B 已在工作集中)
  6. 访问 D:工作集 = {A, B, C, D}
  7. 访问 A:工作集 = {A, B, C, D}(A 已在工作集中)
  8. 访问 E:工作集 = {A, B, D, E}(C 被移出工作集,因为在过去 5 次访问中未被访问)
  9. 访问 F:工作集 = {A, D, E, F}(B 被移出工作集)
  10. 访问 A:工作集 = {A, D, E, F}(A 已在工作集中)
  11. 访问 B:工作集 = {A, B, E, F}(D 被移出工作集)
  12. 访问 C:工作集 = {A, B, C, F}(E 被移出工作集)
  13. 访问 D:工作集 = {A, B, C, D}(F 被移出工作集)

在这个过程中,操作系统通过不断调整工作集的内容,保持进程实际需要的页面在内存中,从而减少页面置换次数,避免抖动现象。

附录A:位示图

举一个8GB内存,块大小为8KB的例子,解释位示图的使用。

我们来详细解释一个8GB内存,块大小为8KB的位图(Bitmap)例子。

计算位图大小

首先,我们需要计算内存总共分为多少个块:

【操作系统】第五章 存储管理_存储体系_18

转换单位:

【操作系统】第五章 存储管理_分区管理_19

【操作系统】第五章 存储管理_夏明亮_20

因此,块数为:

【操作系统】第五章 存储管理_虚拟存储_21

每个块需要一个位来表示其状态(空闲或占用),所以我们需要 1048576 位来表示位图。

位图的存储

由于每个字节有 8 位,所以需要的字节数为:

【操作系统】第五章 存储管理_分区管理_22

位图结构

一个初始的位图可能是这样的(用二进制表示):

00000000 00000000 00000000 ... (总共131072字节,每个字节表示8个内存块的状态)

每一位代表一个8KB的内存块,0表示空闲,1表示占用。

操作示例

  1. 初始状态
  • 位图为全0(所有块都是空闲的)。
  1. 分配 32KB 内存
  • 32KB 内存需要 4 个 8KB 块。
  • 我们需要找到连续的 4 个空闲块,将它们的状态从 0 变为 1。
  • 位图更新示例(假设从第一个字节开始分配):
11110000 00000000 00000000 ... (第一个字节的前4个位变为1,表示前4个块已分配)
  1. 释放 16KB 内存:
  • 16KB 内存对应2个 8KB 块。
  • 将这两个块的状态从 1 变为 0。
  • 位图更新示例(假设释放前两个块):
00110000 00000000 00000000 ... (第一个字节的前2个位变为0)
  1. 分配 8KB 内存:
  • 需要找到一个空闲块,将其状态从 0 变为 1。
  • 位图更新示例(假设分配第1个块):
10110000 00000000 00000000 ... (第一个字节的第1个位变为1)

总结

在这个示例中,我们通过位图来管理 8GB 内存,每个块大小为 8KB。位图的每一位代表一个块的使用情况。操作系统通过扫描位图来找到空闲块并进行分配或释放操作。这种方法简单高效,但在处理大量内存时,位图的扫描开销可能会增加。