一、操作系统的概念和定义
1. 基本特征
2. 目标和功能
3. 发展和分类
4. 运行机制、体系结构
5. 中断和异常
二、进程管理
1. 进程和线程
1.1 进程的概念
在多道程序环境下通过引入进程的概念,更好地描述和控制程序的并发执行,实现操作系统的并发性和共享性。
为此设计了一种数据结构,即进程控制块(PCB)——进程存在的唯一标志,来描述进程的基本情况和运行态。
由程序段、相关数据段、PCB三部分构成进程映像(进程实体)。进程映像是静态的,进程是动态的,创建和撤销进程是指创建和撤销进程的PCB。
1.2 进程的特征
- 动态性(最基本,生命周期)
- 并发性(提高资源利用率)
- 独立性(独立运行、获取资源、接受调度)
- 异步性(时间片、间断性、不可再现,需要进程同步机制)
- 结构性(三部分组成)
1.3 进程的状态和转换
- 运行态(一个处理机同时只有一个进程处于运行态)
- 就绪态(获得了除处理机外的所有资源)
- 阻塞态(等待态,等待获取除处理机外的其他资源)
- 创建态(1、申请一个空白的PCB;2、系统分配运行所需资源;3、初始化PCB;4、转为就绪态 )
- 结束态(先置为结束态,再处理和回收资源)
1.4 进程控制
进程控制用的程序段成为原语,执行期间不允许中断,是不可分割的基本单位。
- 进程的创建
创建态到就绪态的步骤(1234),即创建原语
父子进程的关系:子进程继承父进程的资源,子进程撤销时归还资源给父进程,父进程撤销之前必须撤销其所有子进程
- 进程的终止
引起终止的事件:
正常结束和异常结束
终止原语:
- 根据被终止进程的标识符,检索PCB,从中读出进程的状态;
- 若被终止进程处于运行态,终止并将CPU资源让给其他进程;
- 若该进程还有子进程,则先将其所有子进程终止;
- 将该进程所拥有的全部资源,归还给其父进程或者操作系统;
- 将该PCB从所在队列(链表)删除。
- 进程的阻塞和唤醒
进程的阻塞是主动行为,所以只能由运行态转为阻塞态。
阻塞(Block)原语:
- 找到被阻塞进程的标识号对应的PCB;
- 若处于运行态,保护现场,转为阻塞态,停止运行;
- 将该PCB插入相应事件的等待队列,将资源让给其他就绪进程;
当该进程所期待的事件发生时,将等待该事件的进程唤醒。
唤醒(Wakeup)原语:
- 从等待队列中获取相应进程的PCB;
- 将该PCB从等待队列中移出,并且将状态转为就绪态;
- 把该PCB插入就绪队列,等待调度程序调度。
- 进程切换
进程切换需要操作系统的内核支持。进程切换是指CPU从一个进程转到另一个进程。
进程切换过程:
- 保存处理机上下文(包括程序计数器和其他寄存器);
- 更新PCB信息;
- 将旧进程的PCB移到相应队列(就绪、阻塞队列);
- 选择新进程执行,并更新其PCB;
- 更新内存管理的数据结构;
- 恢复处理机上下文。
注意“调度”和“切换”的区别。
1.5 进程的组织
- 进程控制块
简称PCB
一个系统中往往存在多个进程的PCB,且状态各不相同,多进程的PCB的常见的组织方式是链接方式和索引方式。
链接方式:同一状态的进程PCB链接成一个队列,不同状态的PCB放在不同队列,也可以把阻塞状态的PCB根据不同的阻塞原因分为不同队列;
索引方式:不同状态的PCB关联到不同的索引表中,索引表的表项指向对应的PCB。 - 程序段
可以被多个进程共享,多个进程可以同时运行同一个程序段。 - 数据段
可以是原始数据,也可以是程序处理的中间结果或最终结果。
1.6 进程的通信
指进程之间的信息交换。PV操作通常是低级通信方式(效率低传输量小)。
- 共享存储
操作系统提供共享空间和同步互斥工具(PV操作,系统调用),用户进程自定义读写指令进行数据交换。
进程间的共享空间需要系统调用实现,而线程间的共享内存是自然共享的。 - 消息传递
消息传递系统中,进程间的数据交换是以格式化的消息为单位的。操作系统有发送消息和接收消息两种原语可供调用。
- 两种方式:
- 直接通信(通过消息队列收发消息)
- 间接通信(通过“信箱”)
- 管道通信
“管道”是指用于连接一个写进程和一个读进程以实现它们之间的通信的共享文件,即pipe文件。
管道机制必须有三方面的协调能力:同步,互斥和确认对方存在。
管道是一个缓冲区,大小固定,写满后write()阻塞,读空时read()阻塞,解决写溢出和读完回调的问题。
管道是半双工的,只能单向通信,要实现双向需定义两个管道。
1.7 线程概念和多线程模型
-
线程的基本概念
“轻量级进程”,最基本的CPU执行单元,由线程ID,程序计数器,寄存器集合和堆栈组成。 -
线程与进程的比较
- 调度:线程是独立调度的基本单位,进程是拥有资源的基本单位。
- 拥有资源:线程不拥有大部分系统资源,可以访问所属进程的资源。
- 并发性:进程之间和线程之间都可以并发。
- 系统开销:线程切换不需要资源调度,开销远小于进程切换。
- 地址空间和其他资源:进程间相互独立,线程间共享进程资源。
- 通信方面:进程间需要系统辅助,线程不需要。
- 线程的属性
- 唯一的线程标识符和线程控制块,线程控制块用来记录寄存器和堆栈的现场状态;
- 不同线程可以并发执行同一程序段;
- 同一进程下的线程共享进程资源;
- 不同线程可同时占用不同CPU,CPU调度的基本单位;
- 拥有生命周期
-
线程的实现方式
线程的实现分为用户级线程和内核级线程。
-
多线程模型
实现用户级线程和内核级进程的连接方式。
- 多对一模型
线程管理在用户空间进行,效率较高;但一个线程在使用内核服务时被阻塞则整个进程会被阻塞,且多个线程不能并行再多个CPU上。 - 一对一模型
一个用户级线程阻塞不会影响其他线程;但每创建一个用户线程都需要创建一个内核线程,开销大。 - 多对多模型
将n个用户级线程映射到m个内核级线程,m<=n,克服了上面两种模型的缺点。
1.8 小结
2. 处理器调度
2.1 调度的概念
- 调度的基本概念
处理机调度是对处理机进行分配,即从就绪队列中按照一定的算法(公平、高效)选择一个进程并将处理机分配给它运行,以实现进程并发地执行。
处理机调度是多道程序操作系统的基础,是操作系统设计的核心问题。
- 调度的层次
- 作业调度(高级调度)
从外存选择一个进程分配除cpu外的资源,每个作业只调入调出一次。 - 中级调度(内存调度)
不处于运行态的进程挂到外存,处于挂起态,当具备运行条件且内存空闲,调入内存,挂在就绪队列等待。 - 进程调度(低级调度)
以某种策略从就绪队列选取一个进程分配CPU资源。
- 三级调度联系
2.2 调度时机、切换与过程
以下三种情况不能进程调度和切换:
- 在处理中断的过程中。
- 进程在操作系统内核系统程序临界区。
- 其他需要完全屏蔽终端的原子操作过程中。
应该进行进程调度与切换的情况:
- 发生引起调度条件且当前进程无法继续运行下去时,可以马上进行调度与切换。若操作系统只在这种情况下进行进程调度,则是非剥夺调度。
- 中断处理结束或自陷处理结束后,返回被中断进程的用户态程序执行现场前,若置上请求调度标志,即可马上进行进程调度与切换。若操作系统支持这种情况下的运行调度程序,则实现了剥夺方式的调度。
2.3 进程调度方式
- 非剥夺调度方式(非抢占方式)
当有更加紧急的进程进入就绪队列时当前进程不会立即终止直到进程终止或进入等待态(阻塞态)。实现简单开销小。 - 剥夺调度方式(抢占方式)
采用剥夺式的调度,对提高系统吞吐率和响应效率都有明显的好处。但“剥夺”必须遵循一定的原则,主要有优先权、短进程优先和时间片原则等。
2.4 调度的基本准则
-
CPU利用率
-
系统吞吐量
单位时间系统完成作业的数量。 -
周转时间
从作业提交到作业完成经历的时间。是作业等待、在就绪队列中排队、在处理机上运行及进行输入/输出操作所花费时间的总和。
4. 等待时间
进程处于等待处理机状态的时间之和。用来衡量调度算法的优劣。
5. 响应时间
响应时间指从用户提交请求到系统首次产生响应所用的时间。
2.5 典型算法
- 先来先服务(FCFS)
不可剥夺,对所有作业公平,对长作业有利,适合CPU繁忙。 - 短作业优先(SJF)
短作业优先先选一个或若干估计运行时间最短的进程进入内存,短进程优先从就绪队列选一个估计运行时间最短的进程进入运行态。
对长作业不利,没有考虑紧迫性,估计值不一定准确。平均等待时间,平均周转时间最短。 - 优先级调度
非剥夺式优先级调度/剥夺式优先级调度–高优先级能否抢占
静态优先级/动态优先级–进程创建后优先级是否可以改变
优先级原则参考:
- 系统进程>用户进程
- 交互型进程>非交互型进程
- IO型进程>计算型进程
- 高响应比优先调度
- 时间片轮转
对就绪队列进行FCFS调度,到达指定时间片后切换进程,未执行完的进程被剥夺资源后再次进入就绪队列等待。 - 多级反馈队列
分多个队列,每个队列优先级和时间片不同,进程在多个队列间轮转。
3. 进程同步
3.1 进程同步的基本概念
- 临界资源
临界资源可以被共享,但只能互斥访问。在每个进程中访问临界资源的代码段叫做临界区。
对临界资源的访问过程分为四部分:
- 同步(直接制约关系)
进程间为了完成某个任务需要按特定次序等待、传递信息的相互协作的过程。 - 互斥(间接制约关系)
相对于临界资源而言的。
为禁止两进程同时进入临界区,同步机制应遵循以下准则:
- 空闲礼让
- 忙则等待
- 有限等待
- 让权等待
3.2 临界区互斥的方法
- 软件实现
- 单标志法
- 双标志法先检查
- 双标志法后检查
- Peterson’s Algorithm
- 硬件实现
- 中断屏蔽方法
关中断,限制了处理机交替执行的能力,效率低,关中断的权利交给用户不安全。 - 硬件指令方法
TestAndSet() Swap()等原子操作。
3.3 信号量
用于解决同步和互斥问题,只能被wait(S)和signal(S)两个原语访问,记为P操作和V操作。
-
整型信号量
缺点:while忙等。 -
记录型信号量
-
利用信号量实现同步
-
利用信号量实现互斥
-
利用信号量实现前驱关系
-
分析进程同步和互斥问题的方法步骤
3.4 管程
- 定义
- 条件变量
3.5 经典同步问题
-
生产者-消费者问题
问题描述∶一组生产者进程和一组消费者进程共享一个初始为空、大小为n的缓冲区,只有缓冲区没满时,生产者才能把消息放入缓冲区,否则必须等待;只有缓冲区不空时,消费者才能从中取出消息,否则必须等待。由于缓冲区是临界资源,它只允许一个生产者放入消息,或一个消费者从中取出消息。 -
读者-写者问题
- 问题描述∶ 有读者和写者两组并发进程,共享一个文件,当两个或以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。因此要求∶① 允许多个读者可以同时对文件执行读操作;② 只允许一个写者往文件中写信息;③ 任一写者在完成写操作之前不允许其他读者或写者工作;④ 写者执行写操作前,应让已有的读者和写者全部退出。
- 思路:首先设置信号量count为计数器,用于记录当前读者的数量,初值为0;设置 mutex为互斥信号量,用于保护更新 count 变量时的互斥;设置互斥信号量rw,用于保证读者和写者的互斥访问。
- 代码:
上面的代码保证读进程优先,写进程可能会出现饥饿现象。-- rw初始为1
若要保证写进程优先级和读进程一样,可以在读写进程上再加一个信号量确保无写进程时读进程才能计数并阻塞写进程。
- 哲学家进餐问题
问题描述∶一张圆桌边上坐着 5名哲学家,每两名哲学家之间的桌上摆一根筷子,两根筷子中间是一碗米饭,如图2.10 所示。哲学家们倾注毕生精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,才试图拿起左、右两根筷子(一根一根地拿起)。若筷子已在他人手上,则需要等待。饥饿的哲学家只有同时拿到了两根筷子才可以开始进餐,进餐完毕后,放下筷子继续思考。
关键是如何让一名哲学家拿到左右两根筷子而不造成死锁或饥饿现象。
解决方法有两个∶一是让他们同时拿两根筷子;二是对每名哲学家的动作制定规则,避免饥饿或死锁现象的发生。
4. 吸烟者问题
问题描述∶假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它,但要卷起并抽掉一支烟,抽烟者需要有三种材料∶烟草、纸和胶水。三个抽烟者中,第一个拥有烟草,第二个拥有纸,第三个拥有胶水。供应者进程无限地提供三种材料,供应者每次将两种材料放到桌子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它,并给供应者一个信号告诉已完成,此时供应者就会将另外两种材料放到桌上,如此重复(让三个抽烟者轮流地抽烟)。
3.6 小结
4. 死锁
4.1 死锁的定义
多个进程并发执行的问题——死锁。所谓死锁,是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
4.2 死锁产生的原因
- 系统资源的竞争
- 进程推进顺序非法
- 死锁产生的必要条件
- 互斥
- 不剥夺
- 请求并保持
- 循环等待
4.3 死锁的处理策略
4.3.1 死锁预防
设置某些限制条件,破坏产生死锁的4个必要条件中的一个或几个,以防止发生死锁
- 破坏互斥:
一些场景下互斥性必须要满足。 - 破坏不剥夺:
实现比较复杂,释放已获得的资源可能造成前一阶段工作的失效,反复地申请和释放资源会增加系统开销,降低系统吞吐量。这种方法常用于状态易于保存和恢复的资源,如 CPU的寄存器及内存资源,一般不能用于打印机之类的资源。 - 破坏请求与保持:
预先静态分配方法,即进程在运行前一次申请完它所需要的全部资源,在它的资源未满足前,不把它投入运行。
缺点是资源有效利用率低,资源长期占用容易导致饥饿现象。 - 破坏循环等待:
顺序资源分配法。首先给系统中的资源编号,规定每个进程必须按编号递增的顺序请求资源,同类资源一次申请完。也就是说,只要进程提出申请分配资源 Ri 则该进程在以后的资源申请中就只能申请编号大于 Ri 的资源。
缺点:编号必须相对稳定,限制了新类型设备的增加;会发生作业使用资源的顺序与系统规定顺序不同的情况,造成资源的浪费。
4.3.2 死锁避免
在资源的动态分配过程中,用某种方法防止系统进入不安全状态,从而避免死锁。
- 系统安全状态
含义:允许进程动态地申请资源,但系统在进行资源分配之前,先计算此次分配的安全性。若此次分配不会导致系统进入不安全状态,则允许分配;否则让进程等待。
安全状态:指系统能按某种进程推进顺序(P1,P2,…,Pn)为每个进程Pi分配其所需的资源,直至满足每个进程对资源的最大需求,使每个进程都可顺序完成。此时称P1,P2,…,Pn为安全序列。
若系统无法找到一个安全序列,则称系统处于不安全状态。
不安全状态不一定会导致死锁,反之安全状态下系统就能避免死锁。
举例:P2,P1,P3就是一个安全序列。
- 银行家算法
- 数据结构:
- 算法描述:
- 安全性算法:
4.3.3 死锁的检测及解除
无须采取任何限制性措施,允许进程在运行过程中发生死锁。通过系统的检测机构及时地检测出死锁的发生,然后采取某种措施解除死锁。
- 资源分配图
系统死锁可利用资源分配图来描述。
用圆圈代表一个进程,用框代表一类资源。
由于一种类型的资源可能有多个,因此用框中的一个圆代表一类资源中的一个资源。
从进程到资源的有向边称为请求边,表示该进程申请一个单位的该类资源;从资源到进程的边称为分配边,表示该类资源已有一个资源分配给了该进程。
在下面的资源分配图中,进程 P1 已经分得了两个 R1 资源,并又请求一个 R2 资源;进程 P2 分得了一个 R1 资源和一个 R2资源,并又请求一个 R1 资源。
- 死锁定理
简化资源分配图可检测系统状态 S 是否为死锁状态。
S为死锁的条件是当且仅当 S 状态的资源分配图是不可完全简化的,该条件为死锁定理。
简化条件:
1)在资源分配图中,找出既不阻塞又不孤点的进程 P,即资源满足请求的进程,消去它所有的请求边和分配边,使之成为孤立的结点。
在上图中进程P1满足条件。
2)满足第一步的进程 Pi 所释放的资源,可以唤醒某些因等待这些资源而阻塞的进程,原来的阻塞进程可能变为非阻塞进程。
根据1)中的方法进行一系列简化后,若能消去图中所有的边,则称该图是可完全简化的。
在上图中进程P2满足条件。
- 死锁解除
死锁解除的主要方法有∶
1)资源剥夺法。
挂起某些死锁进程,并抢占它的资源,将这些资源分配给其他的死锁进程。但应防止被挂起的进程长时间得不到资源而处于资源匮乏的状态。
2)撤销进程法。
强制撤销部分甚至全部死锁进程并剥夺这些进程的资源。撤销的原则可以按进程优先级和撤销进程代价的高低进行。
3)进程回退法。
让一(或多)个进程回退到足以回避死锁的地步,进程回退时自愿释放资源而非被剥夺。要求系统保持进程的历史信息,设置还原点。
三、 内存管理
3.1 内存管理概念
3.1.1 基本原理和要求
内存管理的功能有∶
- 内存空间的分配与回收
- 地址转换
- 内存空间的扩充
- 存储保护
1. 程序装入与链接
创建进程首先要将程序和数据装入内存。将用户源程序变为可在内存中执行的程序,通常需要以下几个步骤∶
- 编译:由编译程序将用户源代码编译成若干目标模块。
- 链接:由链接程序将编译后形成的一组目标模块及所需的库函数链接在一起,形成一个完整的装入模块。
链接的三种方式:
- 静态链接
程序运行前,先将各目标模块及它们所需的库函数链接成一个完整的可执行程序,以后不再拆开。- 装入时动态链接
将用户源程序编译后所得到的一组目标模块,在装入内存时,采用边装入边链接的方式。- 运行时动态链接
对某些目标模块的链接,是在程序执行中需要该目标模块时才进行的。其优点是便于修改和更新,便于实现对目标模块的共享。
- 装入:由装入程序将装入模块装入内存运行。
装入的三种方式:
- 绝对装入:
绝对装入程序按照装入模块中的地址,将程序和数据装入内存,程序中的逻辑地址与实际内存地址完全相同。只适用于单道程序环境。通常情况下在程序中采用的是符号地址,编译或汇编时再转换为绝对地址。
多道程序环境下,多个目标模块的起始地址(简称始址)通常都从 0开始,程序中其他地址都是相对于始址的,此时应采用可重定位装入方式根据内存的当前情况,将装入模块装入内存的适当位置。
- 可重定位装入(静态重定位):
装入时对目标程序中指令和数据的修改过程称为重定位,地址变换通常是在装入时一次完成的。
静态重定位的特点:一个作业装入内存时,必须给它分配要求的全部内存空间,若没有足够的内存,则不能装入该作业;此外,作业一旦进入内存,整个运行期间就不能在内存中移动,也不能再申请内存空间。- 动态运行时装入(动态重定位):
程序在内存中若发生移动,则需要采用动态的装入方式。装入程序把装入模块装入内存后,把地址变换推迟到程序真正要执行时才进行。因此装入内存后的所有地址均为相对地址。这种方式需要一个重定位寄存器的支持。
动态重定位的特点∶可以将程序分配到不连续的存储区中;在程序运行之前可以只装入它的部分代码即可投入运行,然后在程序运行期间,根据需要动态申请分配内存;便于程序段的共享,可以向用户提供一个比存储空间大得多的地址空间。
2. 逻辑地址空间和物理地址空间
- 逻辑地址空间
编译后,每个目标模块都从0号单元开始编址,这称为该目标模块的相对地址(或逻辑地址)。
当链接程序将各个模块链接成一个完整的可执行目标程序时,链接程序顺序依次按各个模块的相对地址构成统一的从0号单元开始编址的逻辑地址空间。
不同进程可以有相同的逻辑地址,因为这些相同的逻辑地址可以映射到主存的不同位置。 - 物理地址空间
物理地址空间是指内存中物理单元的集合,它是地址转换的最终地址,进程在运行时执行指令和访问数据,最后都要通过物理地址从主存中存取。
当装入程序将可执行代码装入内存时,必须通过地址转换将逻辑地址转换成物理地址,这个过程称为地址重定位。
3. 内存保护
内存分配前,需要保护操作系统不受用户进程的影响,同时保护用户进程不受其他用户进程的影响。内存保护可采取两种方法∶
1)在CPU中设置一对上、下限寄存器,存放用户作业在主存中的下限和上限地址,每当CPU要访问一个地址时,分别和两个寄存器的值相比,判断有无越界。
2)采用重定位寄存器(或基址寄存器)和界地址寄存器(又称限长寄存器)来实现这种保护。
重定位寄存器含最小的物理地址值,界地址寄存器含逻辑地址的最大值。
每个逻辑地址值必须小于界地址寄存器,内存管理机构动态地将逻辑地址与界地址寄存器进行比较,若未发生地址越界,则加上重定位寄存器的值后映射成物理地址,再送交内存单元。
重定位寄存器和界地址寄存器的区别:重定位寄存器是用来"加"的,逻辑地址加上重定位寄存器中的值就能得到物理地址;界地址寄存器是用来"比"的,通过比较界地址寄存器中的值与逻辑地址的值来判断是否越界。
3.1.2 覆盖与交换
1. 覆盖
覆盖的基本思想∶
由于程序运行时并非任何时候都要访问程序及数据的各个部分(尤其是大程序),因此可把用户空间分成一个固定区和若干覆盖区。将经常活跃的部分放在固定区,其余部分按调用关系分段。首先将那些即将访问的段放入覆盖区,其他段放在外存中,在需要调用前,系统再将其调入覆盖区,替换覆盖区中原有的段。
覆盖技术的特点:
打破了必须将一个进程的全部信息装入主存后才能运行的限制,但当同时运行程序的代码量大于主存时仍不能运行;此外,内存中能够更新的地方只有覆盖区的段,不在覆盖区中的段会常驻内存。
2. 交换
交换(对换)的基本思想:
把处于等待状态(或在 CPU 调度原则下被剥夺运行权利)的程序从内存移到辅存,把内存空间腾出来,这一过程又称换出;把准备好竞争CPU运行的程序从辅存移到内存,这一过程又称换入。中级调度采用的就是交换技术。
交换的注意点∶
- 交换需要备份存储,通常是快速磁盘。它必须足够大,并提供对这些内存映像的直接访问。
- 为了有效使用 CPU,需要使每个进程的执行时间比交换时间长,而影响交换时间的主要是转移时间。转移时间与所交换的内存空间成正比。
- 若换出进程,则必须确保该进程完全处于空闲状态。
- 交换空间通常作为磁盘的一整块,且独立于文件系统,因此使用起来可能很快。
- 交换通常在有许多进程运行且内存空间吃紧时开始启动,而在系统负荷降低时就暂停。 普通的交换使用不多,但交换策略的某些变体在许多系统(如UNIX系统)中仍发挥作用。交换技术主要在不同进程(或作业)之间进行,而覆盖则用于同一个程序或进程中。由于覆盖技术要求给出程序段之间的覆盖结构,使得其对用户和程序员不透明,所以对于主存无法存放用户程序的矛盾,现代操作系统是通过虚拟内存技术来解决的。
3.1.3 连续分配管理方式
1. 单一连续分配
内存在此方式下分为系统区和用户区,系统区仅供操作系统使用,通常在低地址部分;用户区是为用户提供的、除系统区之外的内存空间。
这种方式无须进行内存保护。内存中只有一道程序,不会因为访问越界干扰其他程序。
优点是简单、无外部碎片,可以采用覆盖技术,不需要额外的技术支持。
缺点是只能用于单用户、单任务的操作系统中,有内部碎片,存储器的利用率极低。
2. 固定分区分配
固定分区分配是最简单的一种多道程序存储管理方式,它将用户内存空间划分为若干固定大小的区域,每个分区只装入一道作业。
固定分区分配的两种方法:
- 分区大小相等。用于利用一台计算机去控制多个相同对象的场合,缺乏灵活性。
- 分区大小不等。划分为多个较小的分区、适量的中等分区和少量大分区。
为便于内存分配,通常将分区按大小排队,并为之建立一张分区说明表,其中各表项包括每个分区的始址、大小及状态(是否已分配)。当有用户程序要装入时,便检索该表,以找到合适的分区给予分配并将其状态置为"已分配";未找到合适分区时,则拒绝为该用户程序分配内存。
这种分区方式存在两个问题∶
- 一是程序可能太大而放不进任何一个分区中,这时用户不得不使用覆盖技术来使用内存空间;
- 二是主存利用率低,当程序小于固定分区大小时,也占用一个完整的内存分区空间,这样分区内部就存在空间浪费,这种现象称为内部碎片。
固定分区是可用于多道程序设计的最简单的存储分配,无外部碎片,但不能实现多进程共享一个主存区,所以存储空间利用率低。
3. 动态分区分配
动态分区分配又称可变分区分配,是一种动态划分内存的分区方法。这种分区方法不预先划分内存,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。因此,系统中分区的大小和数目是可变的。
动态分区在开始分配时是很好的,但之后会导致内存中出现许多小的内存块。随着时间推移,内存中会产生越来越多的碎片,内存的利用率随之下降。这些小的内存块称为外部碎片,指在所有分区外的存储空间会变成越来越多的碎片。
克服外部碎片可以通过紧凑技术来解决,需要动态重定位寄存器的支持,且相对费时。
动态分区的分配策略:
- 首次适应(First Fit)算法。空闲分区以地址递增的次序链接。分配内存时顺序查找,找 到大小能满足要求的第一个空闲分区。
- 最佳适应(Best Fit)算法。空闲分区按容量递增的方式形成分区链,找到第一个能满足 要求的空闲分区。
缺点:每次都选最小的分区进行分配,会留下越来越多的、很小的、难以利用的内存块。因此这种方法会产生很多的外部碎片。- 最坏适应(Worst Fit)算法。又称最大适应(Largest Fit)算法,空闲分区以容量递减的次序链接,找到第一个能满足要求的空闲分区,即挑选出最大的分区。
缺点:每次都选最大的分区进行分配,虽然可以让分配后留下的空闲区更大,更可用,但是这种方式会导致较大的连续空闲区被迅速用完。如果之后有“大进程”到达,就会没有内存分区可用。- 邻近适应(Next Fit)算法。又称循环首次适应算法,由首次适应算法演变而成。不同之 处是,分配内存时从上次查找结束的位置开始继续查找。
首次适应算法每次都要从头查找,每次都需要检索低地址的小分区。但是这种规则也决定了当低地址部分有更小的分区可以满足需求时,会更有可能用到低地址部分的小分区,也会更有可能把高地址部分的大分区保留下来;
邻近适应算法的规则可能会导致无论低地址、高地址部分的空闲分区都有相同的概率被使用,也就导致了高地址部分的大分区更可能被使用,划分为小分区,最后导致无大分区可用。
3.1.4 非连续分配管理方式
非连续分配方式需要额外的空间去存储分散区域的索引,使得存储密度低于连续存储方式。
非连续分配管理方式根据分区的大小是否固定,分为分页存储管理方式和分段存储管理方式。
在分页存储管理方式中,又根据运行作业时是否要把作业的所有页面都装入内存才能运行,分为基本分页存储管理方式和请求分页存储管理方式。
1. 基本分页存储管理方式
为了尽量避免内存碎片的产生,引入了分页的思想∶把主存空间划分为大小相等且固定的块,块相对较小,作为主存的基本单位。
每个进程也以块为单位进行划分,进程在执行时,以块为单位逐个申请主存中的块空间。
分页的方法从形式上看,像分区相等的固定分区技术,分页管理不会产生外部碎片。
但它有本质的不同点∶块的大小相对分区要小很多,而且进程也按照块进行划分,进程运行时按块申请主存可用空间并执行。进程只会在为最后一个不完整的块申请一个主存块空间时,才产生主存碎片,每个进程平均只产生半个块大小的内部碎片(也称页内碎片)。
- 分页存储的几个基本概念
- ① 页面和页面大小
进程中的块称为页(Page),内存中的块称为页框(Page Frame,或页帧)。外存也以同样的单位进行划分,直接称为块(Block)。
进程在执行时需要申请主存空间,即要为每个页面分配主存中的可用页框,这就产生了页和页框的一一对应。
为方便地址转换,页面大小应是2的整数幂。
页面大小应该适中,页面太小会使进程的页数过多,页表过长,占用大量内存,也会增加硬件地址转换的开销,降低页面换入/换出的效率;页面过大又会使页内碎片增多,降低内存的利用率,要在空间效率和时间效率之间权衡。- ② 地址结构
地址结构包含两部分∶前一部分为页号P,后一部分为页内偏移量W。地址长度为32位,其中 0~11位为页内地址,即每页大小为 4KB;12~31位为页号,地址空间最多允许 20页。- ③ 页表
为了便于在内存中找到进程的每个页面所对应的物理块,系统为每个进程建立一张页表,它记录页面在内存中对应的物理块号,页表一般存放在内存中,由页表项组成。
页表项与地址结构的区别:
页表项与地址都由两部分构成,而且第一部分都是页号,但页表项的第二部分是物理内存中的块号,而地址的第二部分是页内偏移;
页表项的第二部分与地址的第二部分共同组成物理地址。
- 基本地址变换机构
地址变换机构的任务是将逻辑地址转换为内存中的物理地址。地址变换是借助于页表实现的。
分页管理方式存在的两个主要问题∶
① 每次访存操作都需要进行逻辑地址到物理地址的转换,地址转换过程必须足够快,否则访存速度会降低;
② 每个进程引入页表,用于存储映射机制,页表不能太大,否则内存利用率会降低。
- 具有快表的地址变换机构
基本地址变换将页表全部放在内存中,则存取一个数据或一条指令至少要访问两次内存:第一次是访问页表,确定所存取的数据或指令的物理地址;第二次是根据该地址存取数据或指令。这比通常执行指令的速度慢了一半。
为此,在地址变换机构中增设一个具有并行查找能力的高速缓冲存储器——快表,又称相联存储器(TLB),用来存放当前访问的若干页表项,以加速地址变换的过程。与此对应,主存中的页表常称为慢表。具有快表的地址变换机构如下图所示。
- 二级页表
2. 基本分段存储管理方式
-
分段
段式管理方式按照用户进程中的自然段划分逻辑空间。例如,用户进程由主程序、两个子程序、栈和一段数据组成,于是可以把这个用户进程划分为5段,每段从0开始编址,并分配一段连续的地址空间(段内要求连续,段间不要求连续,因此整个作业的地址空间是二维的),其逻辑地址由段号S与段内偏移量W两部分组成。
在页式系统中,逻辑地址的页号和页内偏移量对用户是透明的,但在段式系统中,段号和段内偏移量必须由用户显式提供,在高级程序设计语言中,这个工作由编译程序完成。 -
段表
每个进程都有一张逻辑空间与内存空间映射的段表,其中每个段表项对应进程的一段,段表项记录该段在内存中的始址和长度。段表的内容如图3.14所示。
配置段表后,执行中的进程可通过查找段表,找到每段所对应的内存区。可见,段表用于实现从逻辑段到物理内存区的映射,如图 3.15 所示。
-
地址变换机构
分段系统的地址变换过程如下图所示。为了实现进程从逻辑地址到物理地址的变换功能,在系统中设置了段表寄存器,用于存放段表始址 F 和段表长度 M。从逻辑地址 A 到物理地址 E 之间的地址变换过程如下∶
① 从逻辑地址A中取出前几位为段号 S,后几位为段内偏移量 W,注意在段式存储管理的计算中,逻辑地址一般以二进制数给出,而在页式存储管理中,逻辑地址一般以十进制数给出。
② 比较段号S和段表长度 M,若S≥M,则产生越界中断,否则继续执行。
③ 段表中段号S对应的段表项地址 = 段表始址F+段号S×段表项长度,取出该段表项的前几位得到段长C。若段内偏移量≥C,则产生越界中断,否则继续执行。可以看出,段表项实际上只有两部分,前几位是段长,后几位是始址。
④ 取出段表项中该段的始址 b,计算E=b+W,用得到的物理地址E去访问内存。
- 段的共享与保护
在分段系统中,段的共享是通过两个作业的段表中相应表项指向被共享的段的同一个物理副本来实现的。不能修改的代码称为纯代码或可重入代码(它不属于临界资源),不能修改的代码和数据可以共享,反之不能共享。
与分页管理类似,分段管理的保护方法主要有两种∶一种是存取控制保护,另一种是地址越界保护。
地址越界保护:
将段表寄存器中的段表长度与逻辑地址中的段号比较,若段号大于段表长度,则产生越界中断;再将段表项中的段长和逻辑地址中的段内偏移进行比较,若段内偏移大于段长,也会产生越界中断。分页管理中的地址越界保护只需要判断页号是否越界,页内偏移不可能越界。
与页式管理不同,段式管理不能通过一个整数确定对应的物理地址,因为每段的长度是不固定的,无法通过整数除法得出段号,无法通过求余得出段内偏移,所以段号和段内偏移一定要显式给出(段号,段内偏移),因此分段管理的地址空间是二维的。
3. 段页式管理方式
分页存储管理能有效地提高内存利用率,而分段存储管理能反映程序的逻辑结构并有利于段的共享。将这两种存储管理方法结合起来,便形成了段页式存储管理方式。
在段页式系统中,作业的逻辑地址分为三部分:段号、页号和页内偏移量。
为了实现地址变换,系统为每个进程建立一张段表,每个分段有一张页表。
段表表项中至少包括段号、页表长度和页表始址,页表表项中至少包括页号和块号。
系统中还应有一个段表寄存器,指出作业的段表始址和段表长度(段表寄存器和页表寄存器的作用都有两个,一是在段表或页表中寻址,二是判断是否越界)。
注意∶ 在一个进程中,段表只有一个,而页表可能有多个。
在进行地址变换时,首先通过段表查到页表始址,然后通过页表找到页帧号,最后形成物理地址。如下图所示,进行一次访问实际需要三次访问主存,这里同样可以使用快表来加快查找速度,其关键字由段号、页号组成,值是对应的页帧号和保护码。
所以说,段页式管理的地址空间是二维的。
3.2 虚拟内存管理
3.2.1 基本概念
- 传统存储管理方式的特征
各种内存管理策略都是为了同时将多个进程保存在内存中,以便允许进行多道程序设计。它们都具有以下两个共同的特征∶
一次性和驻留性
许多在程序运行中不用或暂时不用的程序(数据)占据了大量内存空间,而一些需要运行的作业又无法装入运行,显然浪费了内存资源。
- 局部性原理
快表、页高速缓存及虚拟内存技术从广义上讲,都属于高速缓存技术。这个技术所依赖的原理就是局部性原理。局部性原理既适用于程序结构,又适用于数据结构。
局部性原理表现在以下两个方面∶
- 1)时间局部性
程序中的某条指令一旦执行,不久后该指令可能再次执行;某数据被访问过,不久后该数据可能再次被访问。产生时间局部性的典型原因是程序中存在着大量的循环操作。
时间局部性通过将近来使用的指令和数据保存到高速缓冲存储器中,并使用高速缓存的层次结构实现。- 2)空间局部性
一旦程序访问了某个存储单元,在不久后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,因为指令通常是顺序存放、顺序执行的,数据也一般是以向量、数组、表等形式簇聚存储的。
空间局部性通常使用较大的高速缓存,并将预取机制集成到高速缓存控制逻辑中实现。
虚拟内存技术实际上建立了"内存-外存"的两级存储器结构,利用局部性原理实现高速缓存。
- 虚拟存储器的定义和特征
基于局部性原理,在程序装入时,将程序的一部分装入内存,而将其余部分留在外存,就可启动程序执行。在程序执行过程中,当所访问的信息不在内存时,由操作系统将所需要的部分调入内存,然后继续执行程序。
另一方面,操作系统将内存中暂时不使用的内容换出到外存上,从而腾出空间存放将要调入内存的信息。
这样,系统好像为用户提供了一个比实际内存大得多的存储器,称为虚拟存储器。
虚拟存储器的大小由计算机的地址结构决定,并不是内存和外存的简单相加。
虚拟存储器有以下三个主要特征∶
- 1)多次性。无须在作业运行时一次性地全部装入内存,而允许被分成多次调入 内存运行。
- 2)对换性。无须在作业运行时一直常驻内存,而允许在作业的运行过程中,进 行换进和换出。
- 3)虚拟性。从逻辑上扩充内存的容量,使用户所看到的内存容量远大于实际的 内存容量。
- 虚拟内存技术的实现
虚拟内存技术允许将一个作业分多次调入内存。采用连续分配方式时,会使相当一部分内存空间都处于暂时或"永久"的空闲状态,造成内存的浪费,而且也无法从逻辑上扩大内存容量。因此,虚拟内存的实现需要建立在离散分配的内存管理方式的基础上。
虚拟内存的实现有以下三种方式∶
● 请求分页存储管理。
● 请求分段存储管理。
● 请求段页式存储管理。
不管哪种方式,都需要有一定的硬件支持。 一般需要的支持有以下几个方面∶
● 一定容量的内存和外存。
● 页表机制(或段表机制),作为主要的数据结构。
● 中断机构,当用户程序要访问的部分尚未调入内存时,则产生中断。
● 地址变换机构,逻辑地址到物理地址的变换。
3.2.2 请求分页管理方式
- 页表机制
请求分页系统在一个作业运行之前不要求一次性调入内存,在作业的运行过程中,如何发现和处理要访问的页面不在内存中的情况是请求分页系统必须解决的两个基本问题。为此,在请求页表项中增加了4个字段。
增加的字段说明如下∶
● 状态位 P。用于指示该页是否已调入内存,供程序访问时参考。
● 访问字段 A。用于记录本页在一段时间内被访问的次数,或记录本页最近已有多长时间未被访问,供置换算法换出页面时参考。
● 修改位 M。标识该页在调入内存后是否被修改过。
● 外存地址。用于指出该页在外存上的地址,通常是物理块号,供调入该页时参考。 - 缺页中断机制
在请求分页系统中,每当所要访问的页面不在内存中时,便产生一个缺页中断,请求操作系统将所缺的页调入内存。
此时应将缺页的进程阻塞(调页完成唤醒),
若内存中有空闲块,则分配一个块,将要调入的页装入该块,并修改页表中的相应页表项;
若内存中没有空闲块,则要淘汰某页(若被淘汰页在内存期间被修改过,则要将其写回外存)。
缺页中断作为中断,同样要经历诸如保护 CPU 环境、分析中断原因、转入缺页中断处理程序、恢复CPU环境等几个步骤。
与一般的中断相比,它有以下两个明显的区别∶
● 在指令执行期间而非一条指令执行完后产生和处理中断信号,属于内部中断。
● 一条指令在执行期间,可能产生多次缺页中断。 - 地址变换机构
请求分页系统中的地址变换机构,是在分页系统地址变换机构的基础上,为实现虚拟内存,又增加了某些功能而形成的。
在进行地址变换时,先检索快表:
● 若找到要访问的页,则修改页表项中的访问位(写指令还需要重置修改位),然后利用页表项中给出的物理块号和页内地址形成物理地址。
● 若未找到该页的页表项,则应到内存中去查找页表,再对比页表项中的状态位P,看该页是否已调入内存,未调入则产生缺页中断,请求从外存把该页调入内存。
3.2.3 页面置换算法(决定应该换入哪页、换出哪页)
-
最佳(OPT)置换算法
最佳(Optimal,OPT)置换算法选择的被淘汰页面是以后永不使用的页面,或是在最长时间内不再被访问的页面,以便保证获得最低的缺页率。然而,由于无法预知进程在内存下的若干页面中哪个是未来最长时间内不再被访问的,因而该算法无法实现。 -
先进先出(FIFO)页面置换算法
优先淘汰最早进入内存的页面,即在内存中驻留时间最久的页面。该算法实现简单,只需把调入内存的页面根据先后次序链接成队列,设置一个指针总指向最早的页面。但该算法与进程实际运行时的规律不适应,因为在进程中,有的页面经常被访问。
FIFO 算法还会产生所分配的物理块数增大而页故障数不减反增的异常现象,这由 Belady 于 1969年发现,因此称为 Belady异常。只有FIFO算法可能出现 Belady异常,LRU和OPT算法永远不会出现 Belady 异常。 -
最近最少使用(LRU)置换算法
选择最近最长时间未访问过的页面予以淘汰,它认为过去一段时间内未访问过的页面,在最近的将来可能也不会被访问。
该算法为每个页面设置一个访问字段,来记录页面自上次被访问以来所经历的时间,淘汰页面时选择现有页面中值最大的予以淘汰。
- LRU算法根据各页以前的情况,是"向前看"的,而最佳置换算法则根据各页以后的使用情况,是"向后看"的。
- LRU 算法的性能较好,但需要寄存器和栈的硬件支持。LRU 是堆栈类的算法。理论上可以证明,堆栈类算法不可能出现 Belady 异常。FIFO 算法基于队列实现,不是堆栈类算法。
- 时钟(CLOCK)置换算法
LRU 算法的性能接近于 OPT算法,但实现起来比较困难,且开销大;FIFO 算法实现简单,但性能差。因此用比较小的开销接近LRU算法的性能,这类算法都是 CLOCK 算法的变体。因为算法要循环扫描缓冲区,像时钟的指针一样转动,所以称为 CLOCK算法。
简单的 CLOCK 算法给每帧关联一个附加位,称为使用位。当某页首次装入主存时,将该帧的使用位设置为 1;当该页随后再被访问到时,其使用位也被置为 1。
对于页替换算法,用于替换的候选帧集合可视为一个循环缓冲区,并有一个指针与之相关联。
当某一页被替换时,该指针被设置成指向缓冲区中的下一帧。当需要替换一页时,操作系统扫描缓冲区,以查找使用位被置为0的一帧。
简单的 CLOCK 算法步骤:
每当遇到一个使用位为1的帧时,操作系统就将该位重新置为0;
若在这个过程开始时,缓冲区中所有帧的使用位均为0,则选择遇到的第一个帧替换;
若所有帧的使用位均为1,则指针在缓冲区中完整地循环一周,把所有使用位都置为 0,并停留在最初的位置上,替换该帧中的页。
由于该算法循环检查各页面的情况,因此称CLOCK算法,又称最近未用(NotRecently Used,NRU)算法。
CLOCK算法的性能比较接近LRU算法,而通过增加使用的位数目,可以使得 CLOCK 算法更加高效。
在使用位的基础上再增加一个修改位,则得到改进型 CLOCK置换算法。
这样,每帧都处于以下 4 种情况之一∶
1)最近未被访问,也未被修改(u= 0,m= 0)
2)最近被访问,但未被修改(u= 1,m= 0)
3)最近未被访问,但被修改(u= 0,m= 1)
4)最近被访问,被修改(u= 1,m= 1)
改进型 CLOCK置换算法步骤:
1)从指针的当前位置开始,扫描帧缓冲区。在这次扫描过程中,对使用位不做任何修改。选择遇到的第一个帧(u=0,m = 0)用于替换。
2)若第1)步失败,则重新扫描,查找(u=0,m=1)的帧。选择遇到的第一个这样的帧用于替换。在这个扫描过程中,对每个跳过的帧,把它的使用位设置成 0。
3)若第2)步失败,则指针将回到它的最初位置,且集合中所有帧的使用位均为0。重复第1)步,并且若有必要,重复第2)步,以便可以找到供替换的帧。
改进型 CLOCK 算法优于简单 CLOCK 算法的地方在于替换时首选没有变化的页。由于修改过的页在被替换之前必须写回,因而这样做会节省时间。
3.2.4 页面分配策略
1. 驻留集大小
对于分页式的虚拟内存,在进程准备执行时,不需要也不可能把一个进程的所有页都读入主存。因此,操作系统必须决定读取多少页,即决定给特定的进程分配几个页框。给一个进程分配的物理页框的集合就是这个进程的驻留集。
需要考虑以下几点:
1)分配给一个进程的存储量越小,任何时候驻留在主存中的进程数就越多,从而可以提高处理机的时间利用效率。
2)若一个进程在主存中的页数过少,则尽管有局部性原理,页错误率仍然会相对较高。
3)若页数过多,则由于局部性原理,给特定的进程分配更多的主存空间对该进程的错误率没有明显的影响。
基于这些因素,现代操作系统通常采用三种策略:
- 1)固定分配局部置换
它为每个进程分配一定数目的物理块,在整个运行期间都不改变。
若进程在运行中发生缺页,则只能从该进程在内存中的页面中选出一页换出,然后调入需要的页面。
实现这种策略时,难以确定应为每个进程分配的物理块数目:太少会频繁出现缺页中断,太多又会使 CPU 和其他资源利用率下降。- 2)可变分配全局置换
这是最易于实现的物理块分配和置换策略,它为系统中的每个进程分配一定数目的物理块,操作系统自身也保持一个空闲物理块队列。当某进程发生缺页时,系统从空闲物理块队列中取出一个物理块分配给该进程,并将欲调入的页装入其中。
这种方法比固定分配局部置换更加灵活,可以动态增加进程的物理块,但也存在弊端,如它会盲目地给进程增加物理块,从而导致系统多道程序的并发能力下降。- 3)可变分配局部置换
它为每个进程分配一定数目的物理块,当某个进程发生缺页时,只允许从该进程在内存的页面中选出一页换出,因此不会影响其他进程的运行。若进程在运行中频繁地缺页,则系统再为该进程分配若干物理块,直至该进程缺页率趋于适当程度;反之,若进程运行中的缺页率特别低,则可适当减少分配给该进程的物理块。
比起可变分配全局置换,这种方法不仅可以动态增加进程物理块的数量,还能动态减少进程物理块的数量,在保证进程不会过多地调页的同时,也保持了系统的多道程序并发能力。当然它需要更复杂的实现,也需要更大的开销,但对比频繁地换入/换出所浪费的计算机资源,这种牺牲是值得的。
2. 调入页面的时机
为确定系统将进程运行时所缺的页面调入内存的时机,可采取以下两种调页策略∶
- 1)预调页策略
根据局部性原理,一次调入若干相邻的页可能会比一次调入一页更高效。但若调入的一批页面中大多数都未被访问,则又是低效的。因此,需要采用以预测为基础的预调页策略,将预计在不久之后便会被访问的页面预先调入内存。但目前预调页的成功率仅约50%。因此这种策略主要用于进程的首次调入,由程序员指出先调入哪些页。 - 2)请求调页策略
进程在运行中需要访问的页面不在内存而提出请求,由系统将所需页面调入内存。由这种策略调入的页一定会被访问,且这种策略比较易于实现,因此在目前的虚拟存储器中大多采用此策略。它的缺点是每次只调入一页,调入/调出页面数多时会花费过多的 I/O 开销。
预调入实际上就是运行前的调入,请求调页实际上就是运行期间调入。一般情况下,两种调页策略会同时使用。
3. 从何处调入页面
请求分页系统中的外存分为两部分:用于存放文件的文件区和用于存放对换页面的对换区。对换区通常采用连续分配方式,而文件区采用离散分配方式,因此对换区的磁盘 I/O 速度比文件区的更快。
从何处调入页面就存在三种情况:
- 1)系统拥有足够的对换区空间
可以全部从对换区调入所需页面,以提高调页速度。为此,在进程运行前,需将与该进程有关的文件从文件区复制到对换区。 - 2)系统缺少足够的对换区空间
凡不会被修改的文件都直接从文件区调入,而当换出这些页面时,由于它们未被修改而不必再将它们换出。
但对于那些可能被修改的部分,在将它们换出时须调到对换区,以后需要时再从对换区调入(因为读的速度比写的速度快)。 - 3)UNIX方式
与进程有关的文件都放在文件区,因此未运行过的页面都应从文件区调入。
曾经运行过但又被换出的页面,由于放在对换区,因此下次调入时应从对换区调入。进程请求的共享页面若被其他进程调入内存,则无须再从对换区调入。
3.2.5 抖动
在页面置换过程中,一种最糟糕的情形是,刚刚换出的页面马上又要换入主存,刚刚换入的页面马上又要换出主存,这种频繁的页面调度行为称为抖动或颠簸。若一个进程的换页时间多于执行时间,则这个进程就在颠簸。
频繁发生缺页中断(抖动)的主要原因是,某个进程频繁访问的页面数目高于可用的物理页帧数目。
虚拟内存技术可在内存中保留更多的进程以提高系统效率。在稳定状态,几乎主存的所有空间都被进程块占据,处理机和操作系统可以直接访问到尽可能多的进程。然而,如果管理不当,那么处理机的大部分时间都将用于交换块,即请求调入页面的操作,而不是执行进程的指令,因此会大大降低系统效率。
3.2.6 工作集
工作集是指在某段时间间隔内,进程要访问的页面集合。基于局部性原理,可以用最近访问过的页面来确定工作集。一般来说,工作集所可由时间f和工作集窗口大小来确定。
3.2.7 地址翻译
四、文件管理
4.1 文件系统管理
4.1.1 文件的概念
1. 文件的定义
文件(File)是操作系统中的一个重要概念。文件是以计算机硬盘为载体的存储在计算机上的信息集合,文件可以是文本文档、图片、程序等。用户的输入输出是以文件为基本单位。
当用户将文件用于应用程序的输入输出时,还希望可以访问文件、修改文件和保存文件等,实现对文件的维护管理,这就需要系统提供一个文件管理系统。
文件的组成:
- 存储空间
- 分类和索引信息
- 访问权限信息
文件系统提供了与二级存储相关的资源的抽象,让用户能在不了解文件的各种属性、文件存储介质的特征及文件在存储介质上的具体位置等情况下,方便快捷地使用文件。
用户通过文件系统建立文件,提供应用程序的输入、输出,对资源进行管理。首先了解文件的结构,通过自底向上的方式来定义。
- 1)数据项
数据项是文件系统中最低级的数据组织形式,可分为以下两种类型∶
基本数据项
用于描述一个对象的某种属性的一个值,如姓名、日期或证件号等,是数据中可命名的最小逻辑数据单位,即原子数据。·
组合数据项
由多个基本数据项组成。
- 2)记录
记录是一组相关的数据项的集合,用于描述一个对象在某方面的属性,如一名考生的报名记录包括考生姓名、出生日期、报考学校代号、身份证号等一系列域。
- 3)文件
文件是指由创建者所定义的一组相关信息的集合,逻辑上可分为有结构文件和无结构文件两种。
在有结构文件中,文件由一组相似的记录组成,如报考某学校的所有考生的报考信息记录,又称记录式文件;
而无结构文件则被视为一个字符流,比如一个二进制文件或字符文件,又称流式文件。
2. 文件的属性
文件具有一定的属性,系统不同,属性也会有所不同,但通常都包括如下属性。
1)名称。文件名称唯一,以容易读取的形式保存。
2)标识符。标识文件系统内文件的唯一标签,通常为数字,是对人不可读的一种内部名称。
3)类型。被支持不同类型的文件系统所使用。
4)位置。指向设备和设备上文件的指针。
5)大小。文件当前大小(用字节、字或块表示),也可包含文件允许的最大值。
6)保护。对文件进行保护的访问控制信息。
7)时间、日期和用户标识。文件创建、上次修改和上次访问的相关信息,用于保护和跟踪文件的使用。
所有文件的信息都保存在目录结构中,而目录结构保存在外存上。文件信息在需要时才调入内存。通常,目录条目包括文件名称及其唯一的标识符,而标识符定位其他属性的信息。
3. 文件的基本操作
文件属于抽象数据类型。为了恰当地定义文件,需要考虑有关文件的操作。操作系统提供系统调用,它对文件进行创建、写、读、重定位、删除和截断等操作。
1)创建文件
创建文件有两个必要步骤∶
一是在文件系统中为文件找到空间;
二是在目录中为新文件创建条目,该条目记录文件名称、在文件系统中的位置及其他可能的信息。
2)写文件
为了写文件,执行一个系统调用,指明文件名称和要写入文件的内容。
对于给定文件名称,系统搜索目录以查找文件位置。系统必须为该文件维护一个写位置的指针。
每当发生写操作时,便更新写指针。
3)读文件
为了读文件,执行一个系统调用,指明文件名称和要读入文件块的内存位置。
同样,需要搜索目录以找到相关目录项,系统维护一个读位置的指针。
每当发生读操作时,更新读指针。
一个进程通常只对一个文件读或写,因此当前操作位置可作为每个进程当前文件位置的指针。由于读和写操作都使用同一指针,因此节省了空间,也降低了系统复杂度。
4)文件重定位(文件寻址)
按某条件搜索目录,将当前文件位置设为给定值,并且不会读、写文件。
5)删除文件
先从目录中找到要删除文件的目录项,使之成为空项,然后回收该文件所占用的存储空间。
6)截断文件
允许文件所有属性不变,并删除文件内容,即将其长度设为0并释放其空间。这6个基本操作可以组合起来执行其他文件操作。
例如,一个文件的复制,可以创建新文件、从旧文件读出并写入新文件。
4. 文件的打开和关闭
打开文件表
因为许多文件操作都涉及为给定文件搜索相关目录条目,因此许多系统要求在首次使用文件时,使用系统调用 open 将指明文件的属性(包括该文件在外存上的物理位置)从外存复制到内存 打开文件表的一个表目中,并将该表目的编号(也称索引)返回给用户。
操作系统维护一个包含所有打开文件信息的表(打开文件表,open-file table)。
当用户需要一个文件操作时,可通过该表的一个索引指定文件,因此省略了搜索环节。当文件不再使用时,进程可以关闭它,操作系统从打开文件表中删除这一条目。
大部分操作系统要求在文件使用之前就被显式地打开。操作 open 会根据文件名搜索目录,并将目录条目复制到打开文件表。
若调用 open 的请求(创建、只读、读写、添加等)得到允许,则进程就可打开文件,而 open 通常返回一个指向打开文件表中的一个条目的指针。通过使用该指针(而非文件名)进行所有 I/O 操作,以简化步骤并节省资源。
注意,在 open 调用完成后,操作系统对该文件的任何操作都不再需要文件名,而只需要 open 调用返回的指针。
整个系统表包含进程相关信息,如文件在磁盘的位置、访问日期和大小。
一个进程打开一个文件,系统打开文件表就会为打开的文件增加相应的条目。当另一个进程执行 open 时,只不过是在其进程打开表中增加一个条目,并指向整个系统表的相应条目。
通常,系统打开文件表的每个文件时,还用一个文件打开计数器(Open Count),以记录多少进程打开了该文件。
每个关闭操作 close 使 count 递减,当打开计数器为0时,表示该文件不再被使用,系统将回收分配给该文件的内存空间等资源。若文件被修改过,则将文件写回外存,并将系统打开文件表中的相应条目删除,最后释放文件的文件控制块(File Control Block,FCB)。
每个打开文件都有如下关联信息∶
● 文件指针
系统跟踪上次的读写位置作为当前文件位置的指针,这种指针对打开文件的某个进程来说是唯一的,因此必须与磁盘文件属性分开保存。
● 文件打开计数
文件关闭时,操作系统必须重用其打开文件表条目,否则表内空间会不够用。因为多个进程可能打开同一个文件,所以系统在删除打开文件条目之前,必须等待最后一个进程关闭文件。计数器跟踪打开和关闭的数量,计数为0时,系统关闭文件,删除该条目。
● 文件磁盘位置
绝大多数文件操作都要求系统修改文件数据。该信息保存在内存中,以免为每个操作都从磁盘中读取。
● 访问权限
每个进程打开文件都需要有一个访问模式(创建、只读、读写、添加等)。该信息保存在进程的打开文件表中,以便操作系统能够允许或拒绝之后的 I/O 请求。
4.1.2 文件的逻辑结构
文件的逻辑结构与存储介质特性无关。文件的逻辑结构实际上是指在文件的内部,数据逻辑上是如何组织起来的。
- 无结构文件(流式文件)
无结构文件是最简单的文件组织形式。
无结构文件将数据按顺序组织成记录并积累、保存,它是有序相关信息项的集合,以 字节(Byte) 为单位。
由于无结构文件没有结构,因而对记录的访问只能通过穷举搜索的方式,因此这种文件形式对大多数应用不适用。
但字符流的无结构文件管理简单,用户可以方便地对其进行操作。
所以,那些对基本信息单位操作不多的文件较适于采用字符流的无结构方式,如源程序文件、目标代码文件等。
- 有结构文件(记录式文件)
有结构文件按记录的组织形式可以分为如下几种∶
1)顺序文件
文件中的记录一个接一个地顺序排列,记录通常是定长的,可以顺序存储或以链表形式存储,在访问时需要顺序搜索文件。
顺序文件有以下两种结构:
第一种是串结构,记录之间的顺序与关键字无关。通常的办法是由时间决定,即按存入时间的先后排列,最先存入的记录作为第1条记录,其次存入的为第2条记录,以此类推。
第二种是顺序结构,指文件中的所有记录按关键字顺序排列。
在对记录进行批量操作,即每次要读或写一大批记录时,顺序文件的效率是所有逻辑文件中最高的;
此外,也只有顺序文件才能存储在磁带上,并能有效地工作,但顺序文件对查找、修改、增加或删除单条记录的操作比较困难。
2)索引文件
索引文件示意图如下图所示:
对于定长记录文件,要查找第 i 条记录,可直接根据下式计算得到第i条记录相对于第1条记录的地址∶
然而,对于可变长记录的文件,要查找第 i 条记录,必须顺序地查找前 i - 1 条记录,从而获得相应记录的长度 L,进而按下式计算出第 i 条记录的首址∶
注意∶假定每条记录前用一个字节指明该记录的长度。
变长记录文件只能顺序查找,系统开销较大。为此,可以建立一张索引表以加快检索速度,索引表本身是定长记录的顺序文件。在记录很多或访问要求高的文件中,需要引入索引以提供有效的访问。实际中,通过索引可以成百上千倍地提高访问速度。
3)索引顺序文件
索引顺序文件是顺序和索引两种组织形式的结合。
索引顺序文件将顺序文件中的所有记录分为若干组,为顺序文件建立一张索引表,在索引表中为每组中的第一条记录建立一个索引项,其中含有该记录的关键字值和指向该记录的指针。
主文件名包含姓名和其他数据项。姓名为关键字,索引表中为每组的第一条记录(不是每条记录)的关键字值,用指针指向主文件中该记录的起始位置。
索引表只包含关键字和指针两个数据项,所有姓名关键字递增排列。主文件中记录分组排列,同一个组中的关键字可以无序,但组与组之间的关键字必须有序。查找一条记录时,首先通过索引表找到其所在的组,然后在该组中使用顺序查找,就能很快地找到记录。
对于含有N条记录的顺序文件,查找某关键字值的记录时,平均需要查找N/2次。
而在索引顺序文件中,假设N条记录分为√N组,索引表中有√N个表项,每组有√N条记录,在查找某关键字值的记录时,先顺序查找索引表,需要查找√N/2次,然后在主文件中对应的组中顺序查找,也需要查找√N/2次,因此共需查找√N/2+√N/2=√N次。
显然,索引顺序文件提高了查找效率,若记录数很多,则可采用两级或多级索引。
索引文件和索引顺序文件都提高了存取的速度,但因为配置索引表而增加了存储空间。
4)直接文件或散列文件(Hash File)
给定记录的键值或通过散列函数转换的键值直接决定记录的物理地址。
这种映射结构不同于顺序文件或索引文件,没有顺序的特性。散列文件有很高的存取速度,但是会引起Hash冲突,即不同关键字的散列函数值相同。
实际上,有结构文件逻辑上的组织,是为在文件中查找数据服务的(顺序查找、索引查找、索引顺序查找、哈希查找)。
4.1.3 目录结构
- 文件控制块和索引结点
为实现目录管理(多文件管理),操作系统中引入了文件控制块的数据结构。
1)文件控制块
文件控制块(FCB)是用来存放控制文件需要的各种信息的数据结构,以实现"按名存取"。
FCB的有序集合称为文件目录,一个FCB就是一个文件目录项。为了创建一个新文件,系统将分配一个 FCB 并存放在文件目录中,成为目录项。
FCB 主要包含以下信息∶
● 基本信息,如文件名、文件的物理位置、文件的逻辑结构、文件的物理结构等。
●存取控制信息,如文件存取权限等。
● 使用信息,如文件建立时间、修改时间等。
2)索引结点
文件描述信息单独形成一个称为索引结点的数据结构,简称i结点。
在文件目录中的每个目录项仅由文件名和指向该文件所对应的 i 结点的指针构成。
存放在磁盘上的索引结点称为磁盘索引结点,UNIX中的每个文件都有一个唯一的磁盘索引结点,主要包括以下几个方面∶
- 文件主标识符,拥有该文件的个人或小组的标识符。
- 文件类型,包括普通文件、目录文件或特别文件。
- 文件存取权限,各类用户对该文件的存取权限。
- 文件物理地址,每个索引结点中含有 13 个地址项,即 iaddr(0)~iaddr(12),它们以直接或间接方式给出数据文件所在盘块的编号。
- 文件长度,以字节为单位。
- 文件链接计数,在本文件系统中所有指向该文件的文件名的指针计数。
- 文件存取时间,本文件最近被进程存取的时间、最近被修改的时间及索引结点最近被修改的时间。
文件被打开时,磁盘索引结点复制到内存的索引结点中,以便于使用。在内存索引结点中又增加了以下内容∶
- 索引结点编号,用于标识内存索引结点。
- 状态,指示 i结点是否上锁或被修改。
- 访问计数,每当有一进程要访问此i结点时,计数加1,访问结束减1。
- 逻辑设备号,文件所属文件系统的逻辑设备号。
- 链接指针,设置分别指向空闲链表和散列队列的指针。
FCB 或索引结点相当于图书馆中图书的索书号,我们可以在图书馆网站上找到图书的索书号,然后根据索书号找到想要的书本。
- 目录结构
- 搜索。当用户使用一个文件时,需要搜索目录,以找到该文件的对应目录项。
- 创建文件。当创建一个新文件时,需要在目录中增加一个目录项。
- 删除文件。当删除一个文件时,需要在目录中删除相应的目录项。
- 显示目录。用户可以请求显示目录的内容,如显示该用户目录中的所有文件及属性。
- 修改目录。某些文件属性保存在目录中,因而这些属性的变化需要改变相应的目录项。
操作时,考虑以下几种目录结构∶
1)单级目录结构
在整个文件系统中只建立一张目录表,每个文件占一个目录项。
2)两级目录结构
单级目录很容易造成文件名称的混淆,因此采用两级方案,将文件目录分成主文件目录(MFD)和用户文件目录(UFD)。
3)多级目录结构(树形目录结构)
将两级目录结构的层次关系加以推广,就形成了多级目录结构,即树形目录结构。
4)无环图目录结构
树形目录结构能便于实现文件分类,但不便于实现文件共享,为此在树形目录结构的基础上增加了一些指向同一结点的有向边,使整个目录成为一个有向无环图。引入无环图目录结构是为了实现文件共享。
4.1.4 文件共享
文件共享使多个用户(进程)共享同一个文件,系统中只需保留该文件的一个副本。
-
基于索引结点的共享方式(硬链接)
在树形结构的目录中,当有两个或多个用户要共享一个子目录或文件时,必须将共享文件或子目录链接到两个或多个用户的目录中,才能方便地找到该文件,如下图所示。
在这种共享方式中,诸如文件的物理地址及其他的文件属性等信息,不再放在目录项中,而放在索引结点中。在文件目录中只设置文件名及指向相应索引结点的指针。在索引结点中还应有一个链接计数count,用于表示链接到本索引结点(即文件)上的用户目录项的数目。 -
基于符号链实现文件共享(软链接)
为使用户 B 能共享用户 A 的一个文件F,可以由系统创建一个 LINK 类型的新文件,也取名为 F,并将文件 F 写入用户 B 的目录中,以实现用户B的目录与文件 F 的链接。在新文件中只包含被链接文件 F 的路径名。这样的链接方法被称为符号链接。
新文件中的路径名只被视为符号链,当用户 B 要访问被链接的文件 F 且正要读 LINK 类新文件时,操作系统根据新文件中的路径名去读该文件,从而实现用户 B 对文件 F 的共享。
在利用符号链方式实现文件共享时,只有文件的拥有者才拥有指向其索引结点的指针。而共享该文件的其他用户只有该文件的路径名,并不拥有指向其索引结点的指针。这样,也就不会发生在文件主删除一个共享文件后留下一个悬空指针的情况。当文件的拥有者把一个共享文件删除后,其他用户通过符号链去访问它时,会出现访问失败,于是将符号链删除,此时不会产生任何影响。
符号链方式存在的问题:
1)一个文件采用符号链方式共享,当文件拥有者将其删除,而在共享的其他用户使用其符号链接访问该文件之前,又在同一路径下创建了另一个具有同样名称的文件,则该符号链将仍然有效,但访问的文件已经改变,从而导致错误。
2)在符号链的共享方式中,当其他用户读共享文件时,需要根据文件路径名逐个地查找目录,直至找到该文件的索引结点。因此,每次访问时,都可能要多次地读盘,使得访问文件的开销变大并增加了启动磁盘的频率。此外,符号链的索引结点也要耗费一定的磁盘空间。
符号链方式的优点:
网络共享只需提供该文件所在机器的网络地址及该机器中的文件路径。
- 上述两种链接方式都存在一个共同的问题,即每个共享文件都有几个文件名。换言之,每增加一条链接,就增加一个文件名。这实质上是每个用户都使用自己的路径名去访问共享文件。当我们试图去遍历整个文件系统时,将会多次遍历到该共享文件。
- 硬链接和软链接都是文件系统中的静态共享方法,在文件系统中还存在着另外的共享需求,即两个进程同时对同一个文件进行操作,这样的共享称为动态共享。
- 硬链接就是多个指针指向一个索引结点,保证只要还有一个指针指向索引结点,索引结点就不能删除;
软链接就是把到达共享文件的路径记录下来,当要访问文件时,根据路径寻找文件。
所以硬链接的查找速度要比软链接的快。
4.1.5 文件保护
文件系统必须控制用户对文件的存取,即解决对文件的读、写、执行的许可问题。为此在文件系统中建立相应的文件保护机制。
文件保护通过口令保护、加密保护和访问控制等方式实现。其中,口令保护和加密保护是为了防止用户文件被他人存取或窃取,而访问控制则用于控制用户对文件的访问方式。
1. 访问类型
对文件的保护可从限制对文件的访问类型中出发。可以控制的访问类型主要有以下几种。
- 读。从文件中读。
- 写。向文件中写。
- 执行。将文件装入内存并执行。
- 添加。将新信息添加到文件结尾部分。
- 删除。删除文件,释放空间。
- 列表清单。列出文件名和文件属性。
此外还可以对文件的重命名、复制、编辑等加以控制。这些高层的功能可以通过系统程序调用上面几种低层系统调用来实现。
2. 访问控制
解决访问控制最常用的方法是根据用户身份进行控制。而实现基于身份访问的最为普通的方法是,为每个文件和目录增加一个访问控制列表(Access-ControlList,ACL),以规定每个用户名及其所允许的访问类型。
这种方法的优点是可以使用复杂的访问方法,缺点是长度无法预计并且可能导致复杂的空间管理,使用精简的访问列表可以解决这个问题。
精简的访问列表采用拥有者、组和其他三种用户类型。
1)拥有者。创建文件的用户。
2)组。一组需要共享文件且具有类似访问的用户。
3)其他。系统内的所有其他用户。
这样,只需用三个域即可列出访问表中这三类用户的访问权限。文件拥有者在创建文件时,说明创建者用户名及所在的组名,系统在创建文件时也将文件主的名字、所属组名列在该文件的 FCB 中。用户访问该文件时,按照拥有者所拥有的权限访问文件,若用户和拥有者在同一个用户组,则按照同组权限访问,否则只能按其他用户权限访问。UNIX操作系统即采用此种方法。
口令和密码是另外两种访问控制方法。
口令指用户在建立一个文件时提供一个口令,系统为其建立 FCB 时附上相应口令,同时告诉允许共享该文件的其他用户。用户请求访问时必须提供相应的口令。这种方法时间和空间的开销不多,缺点是口令直接存在系统内部,不够安全。
密码指用户对文件进行加密,文件被访问时需要使用密钥。这种方法保密性强,节省了存储空间,不过编码和译码要花费一定的时间。
口令和密码都是防止用户文件被他人存取或窃取,并没有控制用户对文件的访问类型。
注意两个问题∶
1)现代操作系统常用的文件保护方法是,将访问控制列表与用户、组和其他成员访问控制方案一起组合使用。
2)对于多级目录结构而言,不仅需要保护单个文件,而且需要保护子目录内的文件,即需要提供目录保护机制。目录操作与文件操作并不相同,因此需要不同的保护机制。
4.2 文件系统实现
4.2.1 文件系统层次结构
- 1、 用户调用接口
此层由若干程序模块组成,每个模块对应一条系统调用。 - 2、文件目录系统
主要功能是管理文件目录,其任务有管理活跃文件目录表、管理读写状态信息表、管理用户进程的打开文件表、管理与组织存储设备上的文件目录结构、调用下一级存取控制模块。 - 3、存取控制验证模块
实现文件保护主要由该级软件完成,它把用户的访问要求与 FCB 中指示的访问控制权限进行比较,以确认访问的合法性。 - 4、逻辑文件系统和文件信息缓冲区
主要功能是根据文件的逻辑结构将用户要读写的逻辑记录转换成文件逻辑结构内的相应块号。 - 5、物理文件系统
主要功能是把逻辑记录所在的相对块号转换成实际的物理地址。 - 6、辅助分配模块
主要功能是管理辅存空间,即负责分配辅存空闲空间和回收辅存空间。 - 7、设备管理程序模块
主要功能是分配设备、分配设备读写用缓冲区、磁盘调度、启动设备、处理设备中断、释放设备读写缓冲区、释放设备等。
4.2.2 目录实现
- 1、线性列表
最简单的目录实现方法是使用存储文件名和数据块指针的线性表。
创建新文件时,必须首先搜索目录表以确定没有同名的文件存在,然后在目录表后增加一个目录项。删除文件则根据给定的文件名搜索目录表,接着释放分配给它的空间。
重用目录项有许多方法∶可以将目录项标记为不再使用,或将它加到空闲目录项表上,还可以将目录表中的最后一个目录项复制到空闲位置,并降低目录表长度。
采用链表结构可以减少删除文件的时间,其优点在于实现简单,不过由于线性表的特殊性,比较费时。 - 2、哈希表
哈希表根据文件名得到一个值,并返回一个指向线性列表中元素的指针。
这种方法的优点是查找非常迅速,插入和删除也较简单,不过需要一些预备措施来避免冲突。最大的困难是哈希表长度固定以及哈希函数对表长的依赖性。 目录查询是通过在磁盘上反复搜索完成的,需要不断地进行I/O操作,开销较大。所以如前所述,为了减少I/O操作,把当前使用的文件目录复制到内存,以后要使用该文件时只需在内存中操作,因此降低了磁盘操作次数,提高了系统速度。
4.2.3 文件实现
1、文件分配方式
1)连续分配
连续分配方法要求每个文件在磁盘上占有一组连续的块,如下图所示。磁盘地址定义了磁盘上的一个线性排序。这种排序使作业访问磁盘时需要的寻道数和寻道时间最小。
连续分配支持顺序访问和直接访问。其优点是实现简单、存取速度快。缺点是文件长度不宜动态增加,因为一旦要增加,可能需要大量移动盘块。此外,反复增删文件后会产生外部碎片。
2)链接分配
链接分配采取离散分配的方式,消除了外部碎片,因此显著提高了磁盘空间的利用率;
又因为根据文件的当前需求为其分配必需的盘块,当文件动态增长时,可以动态地再为它分配盘块,因此无须事先知道文件的大小。
此外,对文件的增、删、改也非常方便。
链接分配又可以分为隐式链接和显式链接两种形式。
隐式链接:每个文件对应一个磁盘块的链表;磁盘块分布在磁盘的任何地方,除最后一个盘块外,每个盘块都有指向下一个盘块的指针,这些指针对用户是透明的。目录包括文件第一块的指针和最后一块的指针。
隐式链接分配的缺点是无法直接访问盘块,只能通过指针顺序访问文件,且盘块指针会消耗一定的存储空间。隐式链接分配的稳定性也是一个问题,系统在运行过程中由于软件或硬件错误导致链表中的指针丢失或损坏,会导致文件数据的丢失。
显式链接:把用于链接文件各物理块的指针,从每个物理块的块末尾中提取出来,显式地存放在内存的一张链接表中。
该表在整个磁盘中仅设置一张,称为文件分配表(File Allocation Table,FAT)。每个表项中存放对应块的下一块链接指针,即下一个盘块号。
3)索引分配
- 链接方案
- 多层索引
- 混合索引
2、文件存储空间管理
1)文件存储器空间的划分与初始化
一般来说,一个文件存储在一个文件卷中。
在一个文件卷中,文件数据信息的空间(文件区)和存放文件控制信息 FCB 的空间(目录区)是分离的。
逻辑卷在提供文件服务前,必须由对应的文件程序进行初始化,划分好目录区和文件区,建
立空闲空间管理表格及存放逻辑卷信息的超级块。
2)文件存储器空间管理
文件存储设备分成许多大小相同的物理块,并以块为单位交换信息,因此,文件存储设备的管理实质上是对空闲块的组织和管理,它包括空闲块的组织、分配与回收等问题。
-
空闲表法
连续分配方式,与内存的动态分配方式类似,为每个文件分配一块连续的存储空间。系统为外存上的所有空闲区建立一张空闲盘块表,每个空闲区对应于一个空闲表项,其中包括表项序号、该空闲区第一个盘块号、该区的空闲盘块数等信息。再将所有空闲区按其起始盘块号递增的次序排列。
-
空闲链表法
将所有空闲盘区拉成一条空闲链,根据构成链所用的基本元素不同,可把链表分成两种形式∶空闲盘块链和空闲盘区链。
空闲盘块链将磁盘上的所有空闲空间以盘块为单位拉成一条链。当用户因创建文件而请求分配存储空间时,系统从链首开始,依次摘下适当数目的空闲盘块分配给用户。当用户因删除文件而释放存储空间时,系统将回收的盘块依次插入空闲盘块链的末尾。这种方法的优点是分配和回收一个盘块的过程非常简单,但在为一个文件分配盘块时可能要重复多次操作。 空闲盘区链将磁盘上的所有空闲盘区(每个盘区可包含若干盘块)拉成一条链。在每个盘区上除含有用于指示下一个空闲盘区的指针外,还应有能指明本盘区大小(盘块数)的信息。分配盘区的方法与内存的动态分区分配类似,通常采用首次适应算法。在回收盘区时,同样也要将回收区与相邻接的空闲盘区合并。 -
位示图法
位示图利用二进制的一位来表示磁盘中一个盘块的使用情况,磁盘上所有的盘块都有一个二进制位与之对应。当其值为"0"时,表示对应的盘块空闲;当其值为"1"时,表示对应的盘块已分配。
-
成组链接法
空闲表法和空闲链表法都不适用于大型文件系统,因为这会使空闲表或空闲链表太大。在 UNIX 系统中采用的是成组链接法,这种方法结合了空闲表和空闲链表两种方法,克服了表太大的缺点。
其大致思想是∶把顺序的n个空闲扇区地址保存在第一个空闲扇区内,其后一个空闲扇区内则保存另一顺序空闲扇区的地址,如此继续,直至所有空闲扇区均予以链接。系统只需要保存一个指向第一个空闲扇区的指针。假设磁盘最初全为空闲扇区,其成组链接如图 4.16 所示。通过这种方式可以迅速找到大批空闲块地址。
4.3 磁盘组织和管理
4.3.1 磁盘结构
- 磁头是否可活动:固定头磁盘、活动头磁盘
- 磁盘是否可更换:固定盘磁盘、可换盘磁盘
4.3.2 磁盘调度算法
一次磁盘读写操作的时间由寻找(寻道)时间、延迟时间和传输时间决定。
1)寻找时间
T
s
T_s
Ts。活动头磁盘在读写信息前,将磁头移动到指定磁道所需要的时间。这个时间除跨越 n 条磁道的时间外,还包括启动磁臂的时间 s,即
T
s
=
m
×
n
+
s
T_s=m\times n+s
Ts=m×n+s
式中,m是与磁盘驱动器速度有关的常数,约为0.2ms,磁臂的启动时间 s 约为2ms。
2)延迟时间
T
r
T_r
Tr。磁头定位到某一磁道的扇区(块号)所需要的时间,设磁盘的旋转速度为 r,则
T
r
=
1
2
r
T_r=\frac{1}{2r}
Tr=2r1
对于硬盘,典型的旋转速度为 5400 转/分,相当于一周11.1ms,则T为 5.55ms;对于软盘,其旋转速度为 300~600 转/分,则 T 为 50~100ms。
3)传输时间
T
t
T_t
Tt。从磁盘读出或向磁盘写入数据所经历的时间,这个时间取决于每次所读/写的字节数 b 和磁盘的旋转速度∶
T
t
=
b
r
N
T_t=\frac{b}{rN}
Tt=rNb
式中,r 为磁盘每秒的转数,N 为一个磁道上的字节数。
在磁盘存取时间的计算中,寻道时间与磁盘调度算法相关(主要因素);延迟时间和传输时间都与磁盘旋转速度线性相关。
磁盘调度算法
(1)先来先服务(First Come First Served,FCFS)算法
FCFS 算法根据进程请求访问磁盘的先后顺序进行调度,这是一种最简单的调度算法,如图4.19 所示。该算法的优点是具有公平性。若只有少量进程需要访问,且大部分请求都是访问簇聚的文件扇区,则有望达到较好的性能;若有大量进程竞争使用磁盘,则这种算法在性能上往往接近于随机调度。所以,实际磁盘调度中会考虑一些更为复杂的调度算法。
(2)最短寻找时间优先(Shortest Seek Time First,SSTF)算法
SSTF 算法选择调度处理的磁道是与当前磁头所在磁道距离最近的磁道,以便使每次的寻找时间最短。当然,总是选择最小寻找时间并不能保证平均寻找时间最小,但能提供比FCFS 算法更好的性能。这种算法会产生"饥饿"现象。如图 4.20所示,若某时刻磁头正在 18号磁道,而在18号磁道附近频繁地增加新的请求,则SSTF算法使得磁头长时间在18号磁道附近工作,将使184 号磁道的访问被无限期地延迟,即被"饿死"。
(3)扫描(SCAN)算法(又称电梯调度算法)
SCAN算法在磁头当前移动方向上选择与当前磁头所在磁道距离最近的请求作为下一次服务的对象,实际上就是在最短寻找时间优先算法的基础上规定了磁头运动的方向,如图4.21所示。由于磁头移动规律与电梯运行相似,因此又称电梯调度算法。SCAN算法对最近扫描过的区域不公平,因此它在访问局部性方面不如 FCFS 算法和 SSTF 算法好。
(4)循环扫描(Circular SCAN, C-SCAN)算法
在扫描算法的基础上规定磁头单向移动来提供服务,回返时直接快速移动至起始端而不服务任何请求。由于SCAN算法偏向于处理那些接近最里或最外的磁道的访问请求,所以使用改进型的 C-SCAN 算法来避免这个问题,如图 4.22 所示。
采用 SCAN算法和C-SCAN算法时,磁头总是严格地遵循从盘面的一端到另一端,显然,在实际使用时还可以改进,即磁头移动只需要到达最远端的一个请求即可返回,不需要到达磁盘端点。这种形式的 SCAN算法和C-SCAN算法称为LOOK调度(见图4.23-1)和C-LOOK(见图 4.23-2)调度,因为它们在朝一个给定方向移动前会查看是否有请求。
4.3.3 磁盘管理
1.磁盘初始化
一个新的磁盘只是一个含有磁性记录材料的空白盘。在磁盘能存储数据之前,它必须分成扇区以便磁盘控制器能进行读和写操作,这个过程称为低级格式化(物理分区)。低级格式化为磁盘的每个扇区采用特别的数据结构。每个扇区的数据结构通常由头、数据区域(通常为 512B 大小)和尾部组成。头部和尾部包含了一些磁盘控制器所使用的信息。
为了使用磁盘存储文件,操作系统还需要将自己的数据结构记录在磁盘上:
第一步将磁盘分为由一个或多个柱面组成的分区(即我们熟悉的C盘、D盘等形式的分区);
第二步对物理分区进行逻辑格式化(创建文件系统),操作系统将初始的文件系统数据结构存储到磁盘上,这些数据结构包括空闲和已分配的空间及一个初始为空的目录。
2.引导块
计算机启动时需要运行一个初始化程序(自举程序),它初始化 CPU、寄存器、设备控制器和内存等,接着启动操作系统。为此,该自举程序应找到磁盘上的操作系统内核,装入内存,并转到起始地址,从而开始操作系统的运行。
自举程序通常保存在 ROM中,为了避免改变自举代码而需要改变 ROM 硬件的问题,因此只在 ROM中保留很小的自举装入程序,将完整功能的自举程序保存在磁盘的启动块上,启动步位于磁盘的固定位。拥有启动分区的磁盘称为启动磁盘或系统磁盘。
3.坏块
由于磁盘有移动部件且容错能力弱,因此容易导致一个或多个扇区损坏。部分磁盘甚至从出厂时就有坏扇区。根据所使用的磁盘和控制器,对这些块有多种处理方式。
对于简单磁盘,如电子集成驱动器(IDE),坏扇区可手工处理,如 MS-DOS 的 Format命令执行逻辑格式化时便会扫描磁盘以检查坏扇区。坏扇区在 FAT表上会标明,因此程序不会使用。
对于复杂的磁盘,如小型计算机系统接口(SCSI),其控制器维护一个磁盘坏块链表。该链表在出厂前进行低级格式化时就已初始化,并在磁盘的整个使用过程中不断更新。低级格式化将一些块保留作为备用,对操作系统透明。控制器可用备用块来逻辑地替代坏块,这种方案称为扇区备用。
对坏块的处理实质上就是用某种机制,使系统不去使用坏块。坏块属于硬件故障,操作系统是不能修复坏块的。
五、输入/输出(I/O)管理
5.1 概述
5.1.1 I/O设备
-
按使用特性分类
1)人机交互类外部设备
用于与计算机用户之间交互的设备,如打印机、显示器、鼠标、键盘等。这类设备的数据交换速度相对较慢,通常是以字节为单位进行数据交换的。
2)存储设备
用于存储程序和数据的设备,如磁盘、磁带、光盘等。这类设备用于数据交换,速度较快,通常以多字节组成的块为单位进行数据交换。
3)网络通信设备
用于与远程设备通信的设备,如各种网络接口、调制解调器等。其速度介于前两类设备之间。网络通信设备在使用和管理上与前两类设备也有很大不同。 -
按传输速率分类
1)低速设备
传输速率仅为每秒几字节到数百字节的一类设备,如键盘、鼠标等。
2)中速设备
传输速率为每秒数千字节至数万字节的一类设备,如行式打印机、激光打印机等。
3)高速设备
传输速率在数百千字节至千兆字节的一类设备,如磁带机、磁盘机、光盘机等。 -
按信息交换的单位分类
1)块设备
由于信息的存取总是以数据块为单位的,所以存储信息的设备称为块设备。它属于有结构设备,如磁盘等。
磁盘设备的基本特征是传输速率较高、可寻址,即对它可随机地读/写任一块。
2)字符设备
用于数据输入输出的设备为字符设备,因为其传输的基本单位是字符。它属于无结构类型,如交互式终端机、打印机等。
基本特征是传输速率低、不可寻址,并且在输入/输出时常采用中断驱动方式。
5.1.2 I/O控制方式
-
程序直接控制方式
计算机从外部设备读取数据到存储器,每次读一个字的数据。对读入的每个字,CPU 需要对外设状态进行循环检查,直到确定该字已经在 I/O 控制器的数据寄存器中。
程序直接控制方式虽然简单且易于实现,但其缺点也显而易见,由于 CPU 和 I/O 设备只能串行工作,导致 CPU 的利用率相当低。 -
中断驱动方式
中断驱动方式的思想是,允许 I/O 设备主动打断 CPU 的运行并请求服务,从而"解放"CPU,使得其向 I/O 控制器发送读命令后可以继续做其他有用的工作。
中断驱动方式比程序直接控制方式有效,但由于数据中的每个字在存储器与I/O控制器之间的传输都必须经过 CPU 的寄存器,这就导致了中断驱动方式仍然会消耗较多的 CPU 时间。 -
DMA 方式
DMA(直接存储器存取)方式的基本思想是在 IO 设备和内存之间开辟直接的数据交换通路,彻底"解放"CPU。
DMA 方式的特点如下∶
1)基本单位是数据块。
2)所传送的数据,是从设备直接送入内存的,或者相反。
3)仅在传送一个或多个数据块的开始和结束时,才需 CPU 干预,整块数据的传送是在DMA 控制器的控制下完成的。
要在主机与控制器之间实现成块数据的直接交换,须在 DMA 控制器中设置如下4类寄存器∶
1)命令/状态寄存器(CR)
用于接收从 CPU 发来的 I/O 命令或有关控制信息,或设备的状态。
2)内存地址寄存器(MAR)
在输入时,它存放把数据从设备传送到内存的起始目标地址;在输出时,它存放由内存到设备的内存源地址。
3)数据寄存器(DR)
用于暂存从设备到内存或从内存到设备的数据。
4)数据计数器(DC)
存放本次要传送的字(节)数。 -
通道控制方式
I/O 通道是指专门负责输入/输出的处理机。I/O 通道方式是 DMA 方式的发展,它可以进一步减少 CPU 的干预,即把对一个数据块的读(或写)为单位的干预,减少为对一组数据块的读(或写)及有关控制和管理为单位的干预。同时,又可以实现 CPU、通道和 I/O 设备三者的并行操作,从而更有效地提高整个系统的资源利用率。
I/O 通道与一般处理机的区别是∶通道指令的类型单一,没有自己的内存,通道所执行的通道程序是放在主机的内存中的,也就是说通道与 CPU 共享内存。
I/O 通道与 DMA 方式的区别是∶ DMA 方式需要 CPU 来控制传输的数据块大小、传输的内存位置,而通道方式中这些信息是由通道控制的。另外,每个 DMA 控制器对应一台设备与内存传递数据,而一个通道可以控制多台设备与内存的数据交换。
5.1.3 I/O子系统的层次结构
1)用户层 I/O 软件
实现与用户交互的接口,用户可直接调用在用户层提供的、与 I/O 操作有关的库函数,对设备进行操作。
2)设备独立性软件
用于实现用户程序与设备驱动器的统一接口、设备命令、设备保护及设备分配与释放等,同时为设备管理和数据传送提供必要的存储空间。
设备独立性又称设备无关性。为此引入逻辑设备和物理设备的概念,应用在请求时使用逻辑设备名请求使用某类设备,在系统执行时将逻辑设备名映射成物理设备名。
使用逻辑设备名的好处:
①增加设备分配的灵活性;
②易于实现IO重定向,所谓I/O重定向,是指用于I/O 操作的设备可以更换(即重定向),而不必改变应用程序。
设备独立性软件的主要功能∶
① 执行所有设备的公有操作;
② 向用户层(或文件层)提供统一接口;
3)设备驱动程序
通常,每类设备配置一个设备驱动程序,它是 I/O进程与设备控制器之间的通信程序,常以进程形式存在。设备驱动程序向上层用户程序提供一组标准接口,设备具体的差别被设备驱动程序所封装(隐藏)。
4)
中断处理程序
用于保存被中断进程的 CPU 环境,转入相应的中断处理程序进行处理,处理完并恢复被中断进程的现场后,返回到被中断进程。
中断处理层的主要任务有∶进行进程上下文的切换,对处理中断信号源进行测试,读取设备状态和修改进程状态等。
5)硬件设备
5.2 I/O核心子系统
5.2.1 I/O子系统概述
I/O核心子系统提供的服务主要有I/O 调度、缓冲与高速缓存、设备分配与回收、假脱机、设备保护和差错处理等。
5.2.2 I/O调度概念
I/O 调度就是确定一个好的顺序来执行这些 I/O 请求。应用程序所发布的系统调用的顺序不一定总是最佳选择,所以需要I/O 调度来改善系统整体性能,使进程之间公平地共享设备访问,减少 I/O 完成所需要的平均等待时间。
上文中的磁盘调度算法就是 I/O 调度的一种。
5.2.3 高速缓存和缓冲区
1.磁盘高速缓存
磁盘高速缓存技术不同于通常意义下的介于 CPU 与内存之间的小容量高速存储器,而是指利用内存中的存储空间来暂存从磁盘中读出的一系列盘块中的信息。因此,磁盘高速缓存逻辑上属于磁盘,物理上则是驻留在内存中的盘块。
高速缓存在内存中分为两种形式∶
一种是在内存中开辟一个单独的存储空间作为磁盘高速缓存,大小固定;
另一种是把未利用的内存空间作为一个缓冲池,供请求分页系统和磁盘I/O时共享。
2.缓冲区
在设备管理子系统中,引入缓冲区的目的主要如下∶
1)缓和 CPU 与 I/O 设备间速度不匹配的矛盾。
2)减少对 CPU 的中断频率,放宽对 CPU 中断响应时间的限制。
3)解决基本数据单元大小(即数据粒度)不匹配的问题。
4)提高 CPU 和 I/O 设备之间的并行性。
其实现方法如下∶
1)采用硬件缓冲器,但由于成本太高,除一些关键部位外,一般不采用硬件缓冲器。
2)采用缓冲区(位于内存区域)。
缓冲区特点:
当缓冲区的数据非空时,不能往缓冲区冲入数据,只能从缓冲区把数据传出;当缓冲区为空时,可以往缓冲区冲入数据,但必须把缓冲区充满后,才能从缓冲区把数据传出。
根据系统设置缓冲器的个数,缓冲技术可以分为如下几种∶
1)单缓冲
2)双缓冲
3)循环缓冲
4)缓冲池
3.两者对比
5.2.4 设备分配与回收
1.设备分配概述
设备分配是指根据用户的 I/O 请求分配所需的设备。分配的总原则是充分发挥设备的使用效率,尽可能地让设备忙碌,又要避免由于不合理的分配方法造成进程死锁。
从设备的特性来看,分为独占设备、共享设备和虚拟设备。
2.设备分配的数据结构
设备分配依据的主要数据结构有设备控制表 (DCT)、控制器控制表 (COCT)、通道控制表 (CHCT) 和系统设备表(SDT),各数据结构功能如下。
设备控制表 (DCT):可以认为,一个设备控制表就表征一个设备,而这个控制表中的表项就是设备的各个属性。
系统设备表 (SDT):整个系统只有一张 SDT,如图 5.10(c)所示。它记录已连接到系统中的所有物理设备的情况,每个物理设备占一个表目。
3.设备分配的策略
1)设备分配原则
设备分配应根据设备特性、用户要求和系统配置情况。
分配的总原则:既
要充分发挥设备的使用效率,又要避免造成进程死锁,还要将用户程序和具体设备隔离开。
2)设备分配方式
设备分配方式有静态分配和动态分配两种。
静态分配主要用于对独占设备的分配,它在用户作业开始执行前,由系统一次性分配该作业所要求的全部设备、控制器(如通道等)。一旦分配,这些设备、控制器(和通道)就一直为该作业所占用,直到该作业被撤销。静态分配方式不会出现死锁,但设备的使用效率低。因此,静态分配方式并不符合分配的总原则。
动态分配在进程执行过程中根据执行需要进行。当进程需要设备时,通过系统调用命令向系统提出设备请求,由系统按照事先规定的策略给进程分配所需要的设备、I/O控制器,一旦用完,便立即释放。动态分配方式有利于提高设备的利用率,但若分配算法使用不当,则有可能造成进程死锁。
3)设备分配算法
常用的动态设备分配算法有先请求先分配、优先级高者优先等。
对于独占设备,既可以采用动态分配方式,又可以采用静态分配方式,但往往采用静态分配方式,即在作业执行前,将作业所要用的这一类设备分配给它。共享设备可被多个进程所共享,一般采用动态分配方式,但在每个 I/O 传输的单位时间内只被一个进程所占有,通常采用先请求先分配和优先级高者优先的分配算法。
4.设备分配的安全性
设备分配的安全性是指设备分配中应防止发生进程死锁。
1)安全分配方式
每当进程发出 I/O 请求后便进入阻塞态,直到其 I/O 操作完成时才被唤醒。
这样,一旦进程已经获得某种设备后便阻塞,不能再请求任何资源,而且在它阻塞时也不保持任何资源。优点是设备分配安全;缺点是 CPU 和 I/O 设备是串行工作的(对同一进程而言)。
2)不安全分配方式
进程在发出 I/O请求后继续运行,需要时又发出第二个、第三个I/O请求等。仅当进程所请求的设备已被另一进程占用时,才进入阻塞态。优点是一个进程可同时操作多个设备,从而迅速推进进程;缺点是这种设备分配有可能产生死锁。
5.逻辑设备名到物理设备名的映射
在系统中设置一张逻辑设备表 (LogicalUnit Table,LUT),用于将逻辑设备名映射为物理设备名。
LUT 表项包括逻辑设备名、物理设备名和设备驱动程序入口地址;
当进程用逻辑设备名来请求分配设备时,系统为它分配相应的物理设备,并在 LUT 中建立一个表项,以后进程再利用逻辑设备名请求 I/O 操作时,系统通过查找 LUT 来寻找相应的物理设备和驱动程序。
5.2.5 SPOOLing 技术(假脱机技术)
1.输入井和输出井
输入井和输出井是指在磁盘上开辟出的两个存储区域。输入井模拟脱机输入时的磁盘,用于收容IO 设备输入的数据。输出井模拟脱机输出时的磁盘,用于收容用户程序的输出数据。
2.输入缓冲区和输出缓冲区
输入缓冲区和输出缓冲区是在内存中开辟的两个缓冲区。输入缓冲区用于暂存由输入设备送来的数据,以后再传送到输入井。输出缓冲区用于暂存从输出井送来的数据,以后再传送到输出设备。
3.输入进程和输出进程
输入进程模拟脱机输入时的外围控制机,将用户要求的数据从输入机通过输入缓冲区再送到输入井。当 CPU需要输入数据时,直接将数据从输入井读入内存。输出进程模拟脱机输出时的外围控制机,把用户要求输出的数据先从内存送到输出井,待输出设备空闲时,再将输出井中的数据经过输出缓冲区送到输出设备。
SPOOLing 系统的主要特点有∶提高了IO 的速度;将独占设备改造为共享设备;实现了虚拟设备功能。