本科生操作系统原理学习记录
学习记录全家桶
第八章 内存管理
8.1 内存管理背景
8.1.1 基本硬件
程序必须装入内存才能被执行
CPU可以直接访问的存储器只有主存高速缓存和寄存器
寄存器通常可在1个(或少于1个)CPU时钟周期内完成访问,完成主存访问可能需要多个CPU时钟周期
CPU暂停(Stall):在读取内存数据时,CPU空闲
Cache 位于主存和CPU寄存器之间,协调速度差异
内存保护需要保证正确的操作
内存管理单元MMU:
-
把虚拟地址映射到物理地址的硬件
-
是CPU用来管理内存的控制线路
-
在MMU策略中,基址寄存器中的值在其送入内存的时候被加入到由一个用户进程所产生的每个地址中
-
用户程序所对应到的是逻辑地址,物理地址对它从来都不可见
内存管理的目的
- 提高内存利用率
- 提高指令执行速率
- 保证指令安全运行
内存管理的功能:
- 内存分配
- 内存回收
- 地址转换
- 存储保护
- 内存扩充
- 内存共享
8.1.2 逻辑和物理地址
逻辑地址Logical address
-
由CPU产生
-
在进程内的相对地址
-
也称:虚拟地址、程序地址
物理地址Physical address
-
内存地址
-
所有内存统一编址
-
也称:绝对地址、实地址
8.1.3 动态加载
进程在调用之前并不加载,使用时才加载
不需要操作系统的特别支持,通过程序设计实现
Windows 的动态链接库
8.1.4 动态链接
动态链接:那些组成程序的目标文件等到程序要运行时才进行链接
-
和各种库文件的链接被推迟到执行时期
-
需要动态装载技术支持
-
需要操作系统支持
8.1.5 地址定位
地址绑定(重定位):在秩序装入内存时,把程序中的相
对地址转换为内存中的绝对地址的过程
指令和数据绑定到内存地址可在三个不同阶段:
编译时期( Compile time)
-
如果内存位置已知,可生成绝对代码
-
如果开始位置改变,需要重新编译代码
加载时期( Load time)
- 如果存储位置在编译时不知,则必须生成可重定位( relocatable )代码
执行时期( Execution time)
-
如果进程执行时可在内存移动,则地址绑定可延迟到运行时
-
需要硬件对地址映射的支持(例如基址和限长寄存器)
8.2 内存分配与回收
8.2.1 连续内存分配
为一个用户程序分配一个连续的内存空间,、
分类:
- 单一连续分配
- 固定分区分配
- 可变分区分配
单一连续分配
分配方式:单道程序环境下,仅装有一道用户程序,即整个内存的用户空间由该程序独占
-
内存分配管理十分简单,内存利用率低
-
用于单用户、单任务OS
-
CP/M、MS-DOS、RT11
未采取存储器保护措施
-
节省硬件
-
方案可行
固定分区分配
最早的、也是最简单的一种可运行多道程序的存储管理方式。
预先把可分配的主存空间分割成若干个连续区域,称为一个分区。
每个分区的大小可以相同也可以不同。但分区大小固定不变,每个分区装一个且只能装一个程序
内存分配:如果有一个空闲分区, 则分配给进程
划分分区的方法:
-
分区大小一样:缺乏灵活性:程序太大,则装不下;程序太小,浪费内存
也有可取之处,某些特定场合可以使用:如某台计算机控制多个大小相同的对象
-
分区大小不一样:多个小分区,适量中分区,少量大分区
可变分区分配
分区(孔、Hole)—可用的内存块,不同大小的分区分布在整个内存中
当一个进程到来的时候,它将从一个足够容纳它分区中分配内存。
操作系统包含以下信息:
-
已分配的分区-已分配分区表
-
空的分区-空闲分区表
分配算法:
- 首次适应算法 First-fit:分配最先找到适合的空闲分区
- 最佳适应算法 Best-fit:搜索整个列表,找到适合条件的最小的分区进行分配
- 最差适应算法 Worst-fit:搜索整个列表,寻找最大的分区进行分配
内存回收
四种可能的情况:
- 回收的分区的前后都是空闲区,合并这三个分区为一个大的空闲区
- 回收的分区的前后都是使用区,此时只用回收当前分区即可
- 回收的分区的前一个分区是空闲,后一个分区为使用区,则合并当前分区与前一个分区为一个大的空闲区
- 回收的分区的后一个分区是空闲,前一个分区为使用区,则合并当前分区与后一个分区为一个大的空闲区
碎片
外碎片 –整个可用内存空间可以用来满足一个请求,但它不是连续的
内碎片 –分配的内存可能比申请的内存大一点,这两者之间的差别是在分区内部,但又不被使用
可通过紧缩来减少外碎片
-
把一些小的空闲内存结合成一个大的块。
-
只有重定位是动态的时候,才有可能进行紧缩,紧缩在执行时期进行
8.2.2 离散内存分配
分页管理
进程物理地址空间可能不连续
- 如果有可用的物理内存,它将分给进程
把物理内存分成大小固定的块,称为帧(Frame)
-
大小为2的幂
-
早期:512字节至8192字节
-
现在:4K-64K
把逻辑内存也分为同样大小的块,称为页(Page)
系统保留所有空闲帧的记录
运行一个有N页大小程序,需要找到N个空帧来装入程序
建立一个页表,把逻辑地址转换为物理地址
存在内碎片
地址转换机制
分页地址被分为:
- 页号:它包含每个页在物理地址之中的基址,用作页表的索引号
- 页偏移:同基址相结合,用来确定送入内存设备的物理内存地址
分页的硬件支持:
内存保护
简单方法:
把页号和页表限长寄存器(PRLR)比较
内存的保护由与每个帧相连的保护位来实现
有效-无效位附在页表的每个表项中:
-
“有效”表示相关的页在进程的逻辑地址空间,并且是一个合法的页
-
“无效”表示页不在进程的逻辑地址空间中
页表中的有效位和无效位
8.3 页表结构管理
8.3.1 两级页表
一个逻辑地址被分为 (在4K页大小的32位机器上)
-
一个20位的页号
-
一个12位的页偏移
由于页表所在页也被分页,页号被进一步分为:
-
一个10位的页号
-
一个10位的页偏移
因此逻辑地址表示如下:
地址转换机制
8.3.2 哈希页表
通常地址空间 > 32 位
虚拟页号被散列到一个页表中。这种页表的每一个条目都包括了一个链表元素,这些元素哈希成同一位置。
虚拟页号与链表中的每个元素相比较,找到匹配项。如果匹配,则相应的物理帧被取出。
8.3.3 反向页表
对于每个真正的内存页或帧有一个条目。
每个条目保存在真正内存位置的页的虚拟地址,以及包括拥有这个页的进程的信息。
反置页表机制:
8.4 分段内存管理
支持用户观点的内存管理机制。
一个程序是一些段的集合,一个段是一个逻辑单位,
分段机制(1)
一个逻辑地址是两个向量的集合:
<segment-number, offset>
段表 - 映射二维用户地址,每个表项包括:
基址 - 包含内存中段物理地址的起始地址
限长 - 指定段的长度
段表基址寄存器(STBR)指向段表在内存中的地址
段表限长寄存器(STLR)表明被一个程序所使用的段的数目
如果 s < STLR,段号s 是合法的
分段硬件:
分段机制(2)
由于段的长度各不相同,内存分配是一个动态存储-分配问题
内存分配
- 首先/最佳适应法
- 外碎片问题
重定位
- 动态
- 由段表来执行
共享
- 共享的段
- 同样的段号
分段机制(3)
保护,每个段表的表项有:
-
有效位 = 0 =>非法段
-
读/写/执行权利
保护位同段相联系,在段的级别进行代码共享
8.5 段页式管理
分段和分页原理的结合
先将用户程序分成若干个段,再把每个段分成若干个页,并为每个段赋予一个段号
逻辑地址:<段号,页号,页内偏移>
这样的结合方法带来:存在内碎片,但是没有外碎片
Intel 386使用段页式管理的例子:
8.6 内存扩充
内存空间不够使用怎么办:
- 紧缩(动态分区)
- 覆盖技术
- 交换技术
- 虚拟内存技术(第九章详细介绍)
8.6.1 覆盖技术
解决问题:程序大小超过物理内存总和
程序执行时
-
只在内存中保留那些在任何时间都需要的指令和数据
-
程序的不同部分在内存中相互替换
由程序员声明覆盖结构,不需要操作系统的特别支持
覆盖结构的程序设计很复杂
应用于早期的操作系统
8.6.2 交换技术
在多道程序环境下,一方面,在内存中的某些进程由于某事件尚未发生而被阻塞运行,但它却占用了大量的内存空间,甚至有时可能出现在内存中所有进程都被阻塞而迫使CPU停止下来等待的情况
另一方面,却又有着许多作业在外存上等待,因无内存而不能进入内存运行的情况
浪费资源,降低系统吞吐量。
一个进程可以暂时被交换(swap)到内存外的一个备份区,随后可以被换回内存继续执行
备份区—是一个固定的足够大的可以容纳所有用户内存映像拷贝的快速磁盘;必须提供对这些内存映像的直接访问
交换技术的思想:内存空间紧张时,将一些进程换出到外存,将外存中一些已经具备运行条件的进程换到内存之中运行。