基本存储管理
- 无存储器抽象
- 一种存储器抽象:地址空间
- 地址空间
- 交换技术
- 空闲内存管理
存储管理
程序员对存储的要求
- 大
- 快
- 不易丢失
存储器分类
- cache:快速、昂贵、数量小
- 主存:中速、中等价格
- 磁盘:慢速、便宜、容量大
存储管理程序处理存储器层次
操作系统中管理存储器层次的部分称为存储器管理程序(memory manager)。它的任务是记录哪些内存在使用,哪些内存是空闲的,在进程需要时为其分配存储空间,在进程使用完毕后释放存储空间,以及当内存无法装入所有进程时,管理内存和磁盘间的交换。
存储管理任务
- 内存分配和回收
- 内存扩充技术
- 重定位
- 存储保护
无存储器抽象
单道程序
最简单的存储抽象就是没有抽象。
早期的大型机(20世纪60年代前)、小型机(70年代前)、个人计算机(80年代前)都没有存储器抽象。每个程序直接访问物理内存。
内存中不能同时运行两个程序。
三种单道程序的存储器模型
同一时刻只运行一道程序,由程序和操作系统共享内存
- 第一种模型曾用于大型机和小型机,不过几乎不再使用了
- 第二种模型用于某些掌上机和嵌入式系统
- 第三种模型曾用于早期的个人计算机(例如,运行MS-DOS),其中位于ROM系统部分称为BIOS(Basic Input Output System,基本输入输出系统)。
组织存储的三种简单方式
多道程序
除了在简单的嵌入式系统中,几乎不再使用单道程序了。大部分现在系统都允许同时运行多个程序。
同时运行多个进程意味着当某个进程在等在I/O结束而阻塞时,另一个进程可以使用CPU,因此,多道程序提高了CPU的利用率。
网络服务器通常可以同时运行多个进程(为不同的客户),不过现在大多数客户(即台式机)机也具备了多道程序能力。
无内存抽象的多道程序
- 使用交换技术:把当前内存中的所有内容保存到磁盘文件中,把下一个程序读入内存运行。保证某一时间内存中只有一个程序。
- 使用硬件的帮助:保护键。利用保护键阻止进程间相互干扰。
一种存储器抽象:地址空间
地址空间的概念
- 8位电话号码的地址空间:0000 0000 ~ 9999 9999
- IPv4地址的地址空间:\(0 \sim 2^{32}-1\)
地址空间
地址空间:一个进程可用于寻址内存的一套地址集合
每个进程都有一个自己的地址空间。并且这个地址空间独立于其他进程的地址空间
进程的地址空间:
- 32位机:\(2^{32}\)字节,即4G字节
- 寻址范围:\(0 \sim 2^{32}-1\)
地址空间VS存储空间
地址空间
源程序经编译后目标程序所在的一个地址范围,同城从0开始——逻辑地址
存储空间
内存中的物理存储单元的集合——物理地址
重定位
逻辑地址到物理地址的映射。在多道程序系统中,内存通常由多个进程共享;进程通常在内存和磁盘间调入、调出。因此,无法确定程序将会载入到内存的什么地方。用户在程序中使用的是逻辑地址。用于内存访问之前要转为物理地址——重定位。
重定位分为两种:
- 静态重定位
- 动态重定位
静态重定位
当程序载入内存时直接修改指令。载入分区1的程序,在每个地址上加100K,载入分区2的程序,在每个地址上加200K,等等。为了在载入时对程序重定位,链接程序必须在产生的二进制代码程序中包含列表或者未影响,由它们指出哪些程序字的地址需要重定位,而哪些时操作码、常熟或者其他一些不需要重定位的项。OS/MFT系统就是这样工作的。
动态重定位—基址寄存器和界限寄存器
基址寄存器:程序的起始物理地址
界限寄存器:程序的长度
在访问内存时,进程生成的每个地址都自动加上基址寄存器的内容。因此,如果基址寄存器的值为100K,CALL 100指令始值上被转换为CALL 100K+100,指令本身不必修改。
边界寄存器自动检查指令,以确保它们没有试图访问当前分区以外的地址。由硬件保护基址和边界寄存器,以防止用户程序修改它们。
固定分区的多道程序
- (a)每个固定内存分区都有一个单独的输入队列
- (b)只有单个输入队列的固定内粗分区
可变分区的多道程序
交换(swapping)
如果计算机的物理内存足够大,可以保存所有进程,那么所有方案都可行
实际上,所有进程所需的RAM数量总和通常要远远超出存储器所能够支持的范围
处理内存超载的方法:
- 交换、覆盖
- 虚拟内存
交换:把一个进程完整地调入内存,使该进程运行一段时间,然后把它存回磁盘。空闲进程主要存储在磁盘上,所以当它们不运行时就不会占用内存。
内存分配随着进程进出内存而变化
- 阴影区域表示的是未使用的内存
当交换在内存中造成很多空洞时,通过将所有进程尽可能地向下移动,以组合成一大块,该技术称为内存压缩(memory compaciton)。该操作不经常进行,因为要耗费大量的CPU时间。例如,一台256MB的计算机40ns可以复制4个字节,它需要花费大约2.7秒钟来压缩全部内存。
-
进程创建或换入时,内存的分配
-
根据进程需要的大小进行分配
-
大部分进程在运行时都要增长,为减少因内存区域不够而引起的进程交换和移动嗦产生的开销,为进程分配一些额外的内存,如下图
(a)为增上的数据段预留空间
(b)为增上的数据段和堆栈段预留空间
内存管理
分配内存时,操作系统必须对其进行管理。由两种方式可用来跟踪内存使用情况:
- 位图
- 链表
使用位图的存储管理
使用位图:内存被划分成分配单元,可能小到几个字,也可能大到几千字节。每个分配单元对应于位图中的一位,0表示空闲,1表示占用(或者相反)。
内存分配:把一个占有K个分配单元的进程调入内存时,存储管理器搜索位图,在为途中找出有K个连续0的串。
分配单元的大小时重要的设计课题。分配单元越小,位图越大。
位图提供了一个简单的方法来记录固定容量的内存字,因为位图的大小只依赖于内存和分配单元的大小。
缺点:查找位图中指定长度的连续0串时耗时的操作。
- (a)有5个进程和3个空闲区的内存
- 刻度表示内存分配单元,阴影区域表示空闲(在位印象中用0表示)
- (b)对应的位图
- (c)用链表表示同样的信息
使用链表的存储管理
使用链表:维护一个记录已分配和空闲内存段的链表。其中,段是指一个进程,或者是两个进程间的空闲区。
表中的每一项都只当了如下内容:空闲区(H)还是进程(P)、起始地址、长度以及指向下一项的指针。
使用链表的存储管理—内存分配
当有进程新建或换入时,要查找链表进行内存分配
最简单的算法时首次适配(first fit)。存储器管理程序沿着段链表搜索,直到找到一个足够大的空闲区。除非该空闲区的大小和要分配的大小正好一样,否则将该空闲区分为两部分,一部分让进程使用,另一部分为未用的内存。首次适配算法是一种快速算法,因为它尽可能少搜索。
对首次适配算法的一个小变种为下次适配(next fit),它的工作方式和首次适配算法相同,不同点在于每次找到合适的空闲区时都记录当时的位置,以便在下次寻找空闲区时从上次结束的地方开始搜索,而不是每次都从头开始。
最佳适配(best fit)算法搜索整个链表,找出足够大的最小空闲区。最佳适配算法试图找出最接近实际需要的空闲区,而不是使用将要需要的大空闲区。
为了避免最接近适配的空闲区回分裂出绩效空洞的问题,有人考虑到最差适配(worst fit),即总是分配最大的空闲区,使分裂出来的空闲区够大从而可以继续使用。
使用链表的存储管理—内存回收
进程X终止前后的四种情况