操作系统原理

计算机系统概述

简介

什么是操作系统?

操作系统(Operating Ststem, OS)是指控制和管理整个计算机系统的硬件和软件资源,并合理地组织调度计算机的工作和资源的分配,以提供给用户和其他软件方便的接口和环境,它是计算机系统中最基本的系统软件。

操作系统在系统中的位置

image

操作系统的功能和目标

  1. 系统资源的管理者
  • 文件管理
  • 内存管理
  • 处理机(CPU)管理
  • 设备(计算机硬件,例如摄像头)管理
  1. 用户和计算机硬件之间的接口

image

  1. 对硬件机器的拓展

操作系统的四个特征

  1. 并发

并发是指两个或多个事件在同一时间间隔内发生。这些事件在宏观上是同时发生的,在微观上是交替发生的。

易混淆的概念——并行:两个或多个事件在同一时刻同时发生

  1. 共享

共享即资源共享,是指系统中的资源内存中多个并发执行的进程共同使用。

image

所谓的“同时”往往是宏观上的,而在微观上,这些进程可能是交替地对该资源进行访问的(即分时共享)

生活实例:

  • 互斥共享方式:使用QQ和微信视频。同一时间段内摄像头只能分配给其中一个进程。
  • 同时共享方式:使用QQ发送文件A,同时使用微信发送文件B。宏观上看,两边都在同时读取并发送文件,说明两个进程都在访问硬盘资源,从中读取数据。微观上看,两个进程是交替着访问硬盘的。
  1. 虚拟

虚拟是指把一个物理上的实体变为若干个逻辑上的对应物。物理实体(前者)是实际存在的,而逻辑上对应物(后者)是用户感受到的。

image

  1. 异步

异步是指,在多道程序环境下,允许多个程序并发执行,但由于资源有限,进程的执行不是一贯到底的,而是走走停停,以不可预知的速度向前推进,这就是进程的异步性。

  • 只有系统拥有并发性,才有可能导致异步性。

操作系统的运行机制和体系结构

  • 指令

image

  • CPU

image

  • 程序

image

  • 操作系统的内核

由于内核划分功能的不同,内核分为大内核和微内核。

image

  • 大内核和微内核的优缺点

image

类比:

  • 操作系统的体系结构问题与企业的管理问题很相似。

  • 内核就是企业的管理层,负责一些重要的工作。只有管理层才能执行特权指令,普通员工只能执行非特权指令。用户态、核心态之间的切换相当于普通员工和管理层之间的工作交接

  • 大内核:企业初创时体量不大,管理层的人会负责大部分的事情。优点是效率高;缺点是组织结构混乱,难以维护。

  • 微内核:随着企业体量越来越大,管理层只负责最核心的一些工作。优点是组织结构清晰,方便维护;缺点是效率低。

中断和异常

概念和作用

中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。

  1. 当中断发生时,CPU立即进入核心态
  2. 当中断发生后,当前运行的进程暂停运行,并由操作系统内核对中断进行处理。
  3. 对于不同的中断信号,会进行不同的处理。
  4. 有了中断,才能实现多道程序并发执行。
  5. “用户态→核心态”是通过中断实现的,并且中断是唯一途径。“核心态→用户态”的切换是通过执行一个特权指令,将程序状态字( PSW)的标志位设置为 “用户态”。

分类

  • 中断信号的来源来自CPU内部称为内中断,外部称为外中断。

image

系统调用

含义

“系统调用”是操作系统提供给应用程序(程序员/编程人员)使用的接口,可以理解为一种可供应用程序调用的特殊函数,应用程序可以发出系统调用请求来获得操作系统的服务。

image

作用

应用程序通过系统调用请求操作系统的服务。系统中的各种共享资源都由操作系统统一掌管,因此在用户程序中,凡是与资源有关的操作(如存储分配、I/o操作、文件管理等),都必须通过系统调用的方式向操作系统提出服务请求,由操作系统代为完成。这样可以保证系统的稳定性和安全性,防止用户进行非法操作。

系统调用和库函数的区别

编程语言(c,java)中里边有很多库函数,其实它们(不是所有的库函数)就是将系统调用封装起来,隐藏一些细节,使上层进行系统调用更加方便。

其他

  • 系统调用发生在用户态,对系统调用的处理发生在核心态。
  • 执行陷入指令(自陷指令或访管指令)会处理内中断,使处理器(CPU)从用户态进入核心态。

进程管理

进程的定义,组成,组织方式,特征

进程

程序: 就是指令序列

引入多道程序(CPU可以并发执行多个程序)之后,为了方便操作系统进行管理,引入了进程,进程实体的概念。

PCB,程序段,数据段三部分构成了进程实体(也叫作进程映像)。一般情况下,我们把进程实体简称为进程。

例如,所谓创建进程,实质上是创建进程实体中的PCB;而撤销进程,实质上是撤销进程实体中的PCB.

PCB是进程存在的唯一标识。

image

进程的组织方式

在一个系统中,通常有数十、数百乃至数千个PCB。为了能对他们加以有效的管理,应该用适当的方式把这些PCB组织起来。
注: 进程的组成讨论的是一个进程内部由哪些部分构成的问题,而进程的组织讨论的是多个进程之间的组织方式问题。

相当于java中的链表和数组。

image

进程的特征

image

进程的状态和转换

状态

进程是程序的一次执行。在这个过程中,进程的状态会有各种变化。为了方便各个进程的管理,操作系统将进程划分为几个状态。

image

除此之外,进程还有两种状态。

image

转换

image

进程控制

含义

进程控制就是要实现进程状态转换。

实现

进程控制由原语实现。所谓原语,一般是指由若干条指令组成的程序段,用来实现某个特定功能,在执行过程中不可被中断

原语采用 “关中断指令” 和 “开中断指令” 来实现。 注意: 原语运行在核心态。

image

那么原语是如何实现进程状态的转换呢?

  1. 更新PCB中的信息(如修改进程状态标志、将运行环境保存到PCB、从PCB恢复运行环境)
    a.所有的进程控制原语一定都会修改进程状态标志
    b.剥夺当前运行进程的CPU使用权必然需要保存其运行环境
    c.某进程开始运行前必然要恢复期运行环境
  2. 将PCB插入合适的队列
  3. 分配/回收资源

具体实现如图所示:

image

进程通信

含义

进程通信就是进程之间的信息交换。

为了保证安全,一个进程不能直接访问另一个进程的地址空间。

操作系统提供了三种方法:共享存储,消息传递,管道通信。

共享存储

image

管道通信

1.管道只能采用半双工通信,某一时间段内只能实现单向的传输。如果要实现双向同时通信,则需要设置两个管道。
2.各进程要互斥地访问管道。
3.数据以字符流的形式写入管道,当管道写满时,写进程的write()系统调用将被阻塞,等待读进程将数据取走。当读进程将数据全部取后,管道变空,此时读进程的read()系统调用将被阻塞。
4.如果没写满,就不允许读。如果没读空,就不允许写。
5.数据一旦被读出,就从管道中被抛弃,这就意味着读进程最多只能有一个,否则可能会有读错数据的情况。

image

消息传递

进程间的数据交换以格式化的消息为单位。进程通过操作系统提供的“发送消息/接收消息” 两个原语进行数据交换。

类似于Http协议。

image

线程概念和多线程模型

概念

有的进程需要同时做很多事,例如用QQ来进行聊天,发送文件等,而传统的进程只能串行执行一系列程序。因此,引入“线程”,来增加并发度。

  • 可以把线程理解为轻量级的进程。

  • 线程是一个基本的CPU执行单元,也是程序执行流的最小单位。

  • 引入线程后,进程作为除CPU之外的系统资源的分配单元。

image

线程分类

  1. 用户级线程
  • 用户级线程由应用程序通过线程库实现。所有的线程管理工作都由应用程序负责(包括线程切换)
  • 用户级线程中,线程切换可以在用户态下即可完成,无需操作系统干预。
  • 在用户看来,是有多个线程。但是在操作系统内核看来,并意识不到线程的存在。(用户级线程对用户不透明,对操作系统透明)
  • 可以这样理解,“用户级线程”就是“从用户视角看能看到的线程”。
  1. 内核级线程
  • 内核级线程的管理工作由操作系统内核完成。线程调度、切换等工作都由内核负责,因此内核级线程的切换必然需要在核心态下才能完成。
  • 可以这样理解,“内核级线程”就是“从操作系统内核视角看能看到的线程”。

==操作系统只“看得见”内核级线程,因此只有内核级线程才是处理机分配的单位。==

多线程模型

多对一模型

  • 多对一模型:多个用户及线程映射到一个内核级线程。每个用户进程只对应一个内核级线程。
  • 优点:用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高
  • 缺点:当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高。多个线程不可在多核处理机上并行运行

image

一对一模型

  • 一对一模型:一个用户级线程映射到一个内核级线程。
  • 优点:当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行。
  • 缺点:一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大。

image

多对多模型

  • 多对多模型:n用户级线程映射到m个内核级线程(n >=m)。每个用户进程对应m个内核级线程。
  • 克服了多对一模型并发度不高的缺点,又克服了一对一模型中一个用户进程占用太多内核级线程,开销太大的缺点。

image

处理机调度的概念和层次

含义

在多道程序系统中,进程的数量往往是多于处理机的个数的,这样不可能同时并行地处理各个进程。

处理机调度,就是从就绪队列中按照一定的算法选择一个进程并将处理机分配给它运行,以实现进程的并发执行。

调度分为三个层次,分别为高级调度,中级调度,初级调度。

高级调度

  • 由于内存空间有限,有时无法将用户提交的作业全部放入内存,因此就需要确定某种规则来决定将作业调入内存的顺序。
  • 高级调度(作业调度)。按一定的原则从外存上处于后备队列的作业中挑选一个(或多个)作业,给他们分配内存等必要资源,并建立相应的进程(建立PCB),以使它(们)获得竞争处理机的权利。
  • 高级调度是辅存(外存)与内存之间的调度。每个作业只调入一次,调出一次。作业调入时会建立相应的PCB,作业调出时才撤销PCB。高级调度主要是指调入的问题,因为只有调入的时机需要操作系统来确定,调出的时机必然是作业运行结束才调出。

image

中级调度

  • 引入了虚拟存储技术之后,可将暂时不能运行的进程调至外存等待。等它重新具备了运行条件且内存又稍有空闲时,再重新调入内存。这么做的目的是为了提高内存利用率和系统吞吐量
  • 暂时调到外存等待的进程状态为挂起状态。值得注意的是,PCB并不会一起调到外存,而是会常驻内存。PCB中会记录进程数据在外存中的存放位置,进程状态等信息,操作系统通过内存中的PCB来保持对各个进程的监控、管理。被挂起的进程PCB会被放到的挂起队列中。
  • 中级调度(内存调度),就是要决定将哪个处于挂起状态的进程重新调入内存。
  • 一个进程可能会被多次调出、调入内存,因此中级调度发生的频率要比高级调度更高。

image

补充知识:进程的七状态模型

  • 暂时被调到外存等待的进程状态称为挂起状态。

  • 挂起状态又可以进一步细分为就绪挂起,堵塞挂起两种状态。

image

低级调度

  • 低级调度(进程调度),其主要任务是按照某种方法和策略从就绪队列中选取一个进程,将处理机分配给它。
  • 进程调度是操作系统中最基本的一种调度,在一般的操作系统中都必须配置进程调度。
  • 进程调度的频率很高,一般几十毫秒一次。

image

三种调度的联系和对比

image

进程调度的时机,切换过程和方式

进程调度的时机

image

临界资源: 一个时间段内只允许一个进程使用的资源。各进程需要互斥的访问临界资源。

临界区:访问临界资源的那段代码。

内核程序临界区一般是用来访问某种内核数据结构的,比如进程的就绪队列。

进程调度的方式

  • 非剥夺调度方式,又称非抢占方式。即,只允许进程主动放弃处理机。在运行过程中即便有更紧迫的任务到达,当前进程依然会继续使用处理机,直到该进程终止或主动要求进入阻塞态。

优点

  • 实现简单,系统开销小但是无法及时处理紧急任务,适合于早期的批处理系统

=============================================================================================================================================================================================================================================================================================================

  • 剥夺调度方式,又称抢占方式。当一个进程正在处理机上执行时,如果有一个更重要或更紧迫的进程需要使用处理机,则立即暂停正在执行的进程,将处理机分配给更重要紧迫的那个进程。

优点

  • 可以优先处理更紧急的进程,也可实现让各进程按时间片轮流执行的功能(通过时钟中断)。适合于分时操作系统、实时操作系统

进程的切换与过程

“狭义的进程调度”与“进程切换”的区别:

  • 狭义的进程调度指的是从就绪队列中选中一个要运行的进程。(这个进程可以是刚刚被暂停执行的进程,也可能是另一个进程,后一种情况就需要进程切换)

  • 进程切换是指一个进程让出处理机,由另一个进程占用处理机的过程。

广义的进程调度包含了选择一个进程和进程切换两个步骤。

进程切换的过程主要完成了:

  • 1.对原来运行进程各种数据的保存

  • 2.对新的进程各种数据的恢复(如:程序计数器、程序状态字、各种数据寄存器等处理机现场信息,这些信息一般保存在进程控制块)

注意 : 进程切换是有代价的,因此如果过于频繁的进行进程调度、切换,必然会使整个系统的效率降低,使系统大部分时间都花在了进程切换上,而真正用于执行进程的时间减少。

调度算法的评价指标

  • CPU利用率: CPU”忙碌”的时间占总时间的比例。
  • 系统吞吐量:单位时间内完成作业的数量。

image

  • 周转时间:是指从作业被提交给系统开始,到作业完成为止的时间间隔。

(作业)周转时间=作业完成时的时间-作业提交时间

平均周转时间=各作业周转时间之和/作业数

由于在周转时间相同的情况下,运行时间不同的作业,给用户的感觉是不一样的,所以提出了带权周转时间的概念。

image

  • 等待时间:指进程/作业处于等待处理机状态时间之和,等待时间越长,用户满意度越低。

对于进程来说,等待时间就是指进程建立后等待被服务的时间之和,在等待I/O完成的期间其实进程也是在被服务的,所以不计入等待时间。
对于作业来说,不仅要考虑建立进程后的等待时间,还要加上作业在外存后备队列中等待的时间。

  • 响应时间:指从用户提交请求到首次产生响应所用的时间。

调度算法(1)

先来先服务(FCFS)

image

短作业优先(SJF)

image

高响应比优先(HRRN)

image

三种算法对比

image

注:这几种算法主要关心对用户的公平性、平均周转时间、平均等待时间等评价系统整体性能的指标,但是不关心“响应时间”,也并不区分任务的紧急程度,因此对于用户来说,交互性很糟糕。因此这三种算法一般适合用于早期的批处理系统,当然,FCFS算法也常结合其他的算法使用,在现在也扮演着很重要的角色。而适合用于交互式系统的调度算法将在下个小节介绍…

调度算法(2)

时间片轮转调度(RR)

image

  • 如果时间片太大,使得每个进程都可以在一个时间片内就完成,则时间片轮转调度算法退化为先来先服务调度算法,并且会增大进程响应时间。因此时间片不能太大。
  • 另一方面,进程调度、切换是有时间代价的(保存、恢复运行环境),因此如果时间片太小,会导致进程切换过于频繁,系统会花大量的时间来处理进程切换,从而导致实际用于进程执行的时间比例减少。可见时间片也不能太小。
  • 一般来说,设计时间片要让切换进程的开销占比不超过1%。

优先级调度算法

image

image

多级反馈队列调度算法

image

  • 通过下边的例子来加深理解

image

总结

比起早期的批处理操作系统来说,由于计算机造价大幅降低,因此之后出现的交互式操作系统(包括分时操作系统、实时操作系统等)更注重系统的响应时间、公平性、平衡性等指标。而这几种算法恰好也能较好地满足交互式系统的需求。因此这三种算法适合用于交互式系统。(比如UNIX使用的就是多级反馈队列调度算法)

进程同步与进程互斥

进程同步

  • 通过进程通信——管道通信的例子来了解什么是进程同步。

image

读进程和写进程并发地运行,由于并发必然导致异步性,因此“写数据”和“读数据”两个操作执行的先后顺序是不确定的。而实际应用中,又必须按照“写数据→读数据”的顺序来执行的。如何解决这种异步问题,就是“进程同步”所讨论的内容。

含义

同步亦称直接制约关系,它是指为完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调它们的工作次序而产生的制约关系。进程间的直接制约关系就是源于它们之间的相互合作。

进程互斥

  • 我们把一个时间段内只允许一个进程使用的资源称为临界资源。
  • 许多物理设备(比如摄像头、打印机)都属于临界资源。此外还有许多变量、数据、内存缓冲区等都属于临界资源。
  • 对临界资源的访问,必须互斥地进行。互斥,亦称间接制约关系。
  • 进程互斥指当一个进程访问某临界资源时,另一个想要访问该临界资源的进程必须等待。当前访问临界资源的进程访问结束,释放该资源之后,另一个进程才能去访问临界资源。

image

对临界资源的互斥访问,可以在逻辑上分为如下四个部分:

image

为了实现对临界资源的互斥访问,同时保证系统整体性能,需要遵循以下原则:

  1. 空闲让进。临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区;
  2. 忙则等待。当已有进程进入临界区时,其他试图进入临界区的进程必须等待;
  3. 有限等待。对请求访问的进程,应保证能在有限时间内进入临界区(保证不会饥饿)
  4. 让权等待。当进程不能进入临界区时,应立即释放处理机,防止进程忙等待。

进程互斥的软件实现方法

单标志法

算法思想:每个进程在访问完临界区后会把使用临界区的权限转交给另一个进程。也就是说每个进程进入临界区的权限只能被另一个进程赋予

代码实现

image

解释

turn的初值为0,即刚开始只允许0号进程进入临界区。
若P1先上处理机运行,则会一直卡在⑤。直到P1的时间片用完,发生调度,切换 P0上处理机运行。代码①不会卡住P0,P0可以正常访问临界区,在 P0访问临界区期间即时切换回P1,P1依然会卡在⑤。只有P0在退出区将turn改为1后,P1才能进入临界区。
因此,该算法可以实现“同一时刻最多只允许一个进程访问临界区”

turn表示当前允许进入临界区的进程号,而只有当前允许进入临界区的进程在访问了临界区之后,才会修改turn的值。

也就是说,对于临界区的访问,一定是按P0→P1→P0→P1→……这样轮流访问。这种必须“轮流访问”带来的问题是,如果此时允许进入临界区的进程是P0,而P0一直不访问临界区,那么虽然此时临界区空闲,但是并不允许P1访问。
因此,单标志法存在的主要问题是:违背“空闲让进”原则。

双标志先检查法

算法思想:设置一个布尔型数组flag[],数组中各个元素用来标记各进程想进入临界区的意愿,比如“flag[0] =ture”意味着0号进程 P0现在想要进入临界区。每个进程在进入临界区之前先检查当前有没有别的进程想进入临界区,如果没有,则把自身对应的标志flag[li]设为true,之后开始访问临界区。

image

若按照①⑤②⑥③⑦….的顺序执行,P0和P1将会同时访问临界区。因此,双标志先检查法的主要问题是:违反“忙则等待”原则。
原因在于,进入区的“检查”和“上锁”两个处理不是一气呵成的。“检查”后,“上锁”前可能发生进程切换。

双标志后检查法

image

Peterson算法

算法思想:双标志后检查法中,两个进程都争着想进入临界区,但是谁也不让谁,最后谁都无法进入临界区。Gary L.Peterson想到了一种方法,如果双方都争着想进入临界区,那可以让进程尝试“孔融让梨”,主动让对方先使用临界区。

image

  • Peterson算法用软件方法解决了进程互斥问题,遵循了空闲让进、忙则等待、有限等待三个原则,但是依然未遵循让权等待的原则。
  • Peterson算法相较于之前三种软件解决方案来说,是最好的,但依然不够好。

进程互斥的硬件实现方法

中断屏蔽方法

image

TestAndSet指令

image

Swap指令

image

信号量机制

什么是信号量

  • 用户进程可以通过使用操作系统提供的一对原语来对信号量进行操作,从而很方便的实现了进程互斥、进程同步。
  • 信号量其实就是一个变量(可以是一个整数,也可以是更复杂的记录型变量),可以用一个信号量来表示系统中某种资源的数量,比如:系统中只有一台打印机,就可以设置一个初值为1的信号量。
  • 原语是一种特殊的程序段,其执行只能一气呵成,不可被中断。原语是由关中断/开中断指令实现的。
  • 一对原语: wait(S)原语和 signal(S)原语,可以把原语理解为我们自己写的函数,函数名分别为 wait和 signal,括号里的信号量s其实就是函数调用时传入的一个参数。
  • wait、signal原语常简称为P、V操作(来自荷兰语proberen和 verhogen)。因此,做题的时候常把wait(S)、 signal(S)两个操作分别写为P(S)、V(S)。

整型信号量

用一个整数型的变量作为信号量,用来表示系统中某种资源的数量。

与普通整数变量的区别:对信号量的操作只有三种,初始化,P操作,V操作。

下面以打印机为例:

image

记录型信号量

整型信号量的缺陷是存在“忙等”问题,因此人们又提出了“记录型信号量”,即用记录型数据结构表示的信号量

image

用信号量实现进程互斥,同步,前驱关系

信号量机制实现进程互斥

  1. 分析并发进程的关键活动,划定临界区(如:对临界资源打印机的访问就应放在临界区)
  2. 设置互斥信号量mutex,初值为1
  3. 在临界区之前执行P(mutex)
  4. 在临界区之后执行V(mutex)

image

注意: 对不同的临界资源(如摄像头,打印机)需要设置不同的互斥信号量。

P、V操作必须成对出现。缺少P(mutex)就不能保证临界资源的互斥访问。缺少V(mutex)会导致资源永不被释放,等待进程永不被唤醒。

信号量机制实现进程同步

进程同步:要让各并发进程按要求有序的进行。

那么如何实现呢?

  1. 分析什么地方需要实现“同步关系”,即必须保证“一前一后”执行的两个操作(或两句代码)
  2. 设置同步信号量s,初始为0
  3. 在“前操作”之后执行v(S)
  4. 在“后操作”之前执行P(S)

下面通过一个例子来解释,要求:进程2的代码4必须在进程1的代码2之后执行。

image

信号量机制实现前驱关系

进程P1中有句代码S1,P2中有句代码S2 …P… P6中有句代码S6。这些代码要求按如下前驱图所示的顺序来执行:
其实每一对前驱关系都是一个进程同步问题(需要保证一前一后的操作),因此,
1.要为每一对前驱关系各设置一个同步变量

2.在“前操作”之后对相应的同步变量执行V操作

3.在“后操作”之前对相应的同步变量执行Р操作

image

生产者——消费者问题

问题描述

系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品并使用。(注:这里的“产品”理解为某种数据)

  • 生产者、消费者共享一个初始为空、大小为n的缓冲区。
  • 只有缓冲区没满时,生产者才能把产品放入缓冲区,否则必须等待。
  • 只有缓冲区不空时,消费者才能从中取出产品,否则必须等待。
  • 缓冲区是临界资源,各进程必须互斥地访问。

image

问题分析

image

如何实现

image

能够改变相邻P,V的顺序

image

多生产者——多消费者

问题描述

桌子上有一只盘子,每次只能向其中放入一个水果。爸爸专向盘子中放苹果,妈妈专向盘子中放橘子,儿子专等着吃盘子中的橘子,女儿专等着吃盘子中的苹果。只有盘子空时,爸爸或妈妈才可向盘子中放一个水果。仅当盘子中有自己需要的水果时,儿子或女儿可以从盘子中取出水果。用PV操作实现上述过程。

image

如何实现

image

问题:可不可以不使用问题信号量?

结论:即使不设置专门的互斥变量mutex,也不会出现多个进程同时访问盘子的现象

原因在于:本题中的缓冲区大小为1,在任何时刻,apple、orange、plate三个同步信号量中最多只有一个是1。因此在任何时刻,最多只有一个进程的P操作不会被阻塞,并顺利地进入临界区…

如果盘子(缓冲区)数量为2,可能会出现两个进程同时访问缓冲区的情况,有可能导致两个进程写入缓冲区的数据相互覆盖的情况。

总结

  1. 在生产者-消费者问题中,如果缓冲区大小为1,那么有可能不需要设置互斥信号量就可以实现互斥访问缓冲区的功能。当然,这不是绝对的,要具体问题具体分析。
  2. 建议:在考试中如果来不及仔细分析,可以加上互斥信号量,保证各进程一定会互斥地访问缓冲区。但需要注意的是,实现互斥的P操作一定要在实现同步的P操作之后,否则可能引起“死锁”。

吸烟者问题

问题描述

假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它,但是要卷起并抽掉一支烟,抽烟者需要有三种材料:烟草、纸和胶水。三个抽烟者中,第一个拥有烟草、第二个拥有纸、第三个拥有胶水。供应者进程无限地提供三种材料,供应者每次将两种材料放桌子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它,并给供应者进程一个信号告诉完成了,供应者就会放另外两种材料再桌上,这个过程一直重复(让三个抽烟者轮流地抽烟)。

image

如何解决

image

读者——写者问题

问题描述

有读者和写者两组并发进程,共享一个文件,当两个或两个以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。因此要求:

①允许多个读者可以同时对文件执行读操作;

②只允许一个写者往文件中写信息;

③任一写者在完成写操作之前不允许其他读者或写者工作;

④写者执行写操作前,应让已有的读者和写者全部退出。

image

如何实现

image

  • 潜在的问题:只要读进程还在读,写进程就要一直堵塞等待,可能会饿死。因此在这种算法中,读进程优先。下面来实现“ 先来先服务”算法,这样就不会导致写进程饿死。

image

总结

读者-写者问题为我们解决复杂的互斥问题提供了一个参考思路。
其核心思想在于设置了一个计数器count用来记录当前正在访问共享文件的读进程数。我们可以用count的值来判断当前进入的进程是否是第一个/最后一个读进程,从而做出不同的处理。
另外,对count变量的检查和赋值不能一气呵成导致了一些错误,如果需要实现“一气呵成”,自然应该想到用互斥信号量。
最后,还要认真体会我们是如何解决“写进程饥饿”问题的。

哲学家吃饭

问题描述

一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭。哲学家们倾注毕生的精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,才试图拿起左、右两根筷子(一根一根地拿起)。如果筷子已在他人手上,则需等待。饥饿的哲学家只有同时拿起两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。

image

问题分析

1.关系分析。系统中有5个哲学家进程,5位哲学家与左右邻居对其中间筷子的访问是互斥关系。

2.整理思路。这个问题中只有互斥关系,但与之前遇到的问题不同的是,每个哲学家进程需要同时持有两个临界资源才能开始吃饭。如何避免临界资源分配不当造成的死锁现象,是哲学家问题的精髓。
3.信号量设置。定义互斥信号量数组chopstick[5]={1,1,1,1,1},用于实现对5个筷子的互斥访问。并对哲学家按0~4编号,哲学家i左边的筷子编号为i,右边的筷子编号为(i+1)%5。

如何实现

  • 如果使用下图所示的方法,则会导致死锁问题。

image

  • 那么如何解决呢?

①可以对哲学家进程施加一些限制条件,比如最多允许四个哲学家同时进餐。这样可以保证至少有一个哲学家是可以拿到左右两只筷子的

②要求奇数号哲学家先拿左边的筷子,然后再拿右边的筷子,而偶数号哲学家刚好相反。用这种方法可以保证如果相邻的两个奇偶号哲学家都想吃饭,那么只会有其中一个可以拿起第一只筷子,另一个会直接阻塞。这就避免了占有一支后再等待另一只的情况。

③仅当一个哲学家左右两支筷子都可用时才允许他抓起筷子。

下面用代码实现第三种方式。

image

管程

为什么引入管程?

信号量机制存在的问题 : 编写程序困难、易出错。 因此人们想设计一种机制,让程序员写程序时不需要再关注复杂的PV操作,让写代码更轻松。1973年,Brinch Hansen首次在程序设计语言(Pascal)中引入了“管程”成分――一种高级同步机制。

管程的定义和基本特征

管程相当于对临界区资源进行抽象而编写的一个类。

管程是一种特殊的软件模块,有这些部分组成:

  1. 局部于管程的共享数据结构说明; (一个类)

  2. 对该数据结构进行操作的一组过程; (类中的方法)

  3. 对局部于管程的共享数据设置初始值的语句; (类中的变量)

  4. 管程有一个名字。 (类名)

管程的基本特征:

  1. 局部于管程的数据只能被局部于管程的过程所访问; (类中变量有自己的作用范围)

  2. **一个进程只有通过调用管程内的过程才能进入管程访问共享数据; ** 这种互斥特性是由编译器来实现的。

  3. 每次仅允许一个进程在管程内执行某个内部过程。

java中类似于管程的机制(单例模式)

image

死锁

含义

​ 在并发环境下,各进程因竞争资源而造成的一种互相等待对方手里的资源,导致各进程都阻塞,都无法向前推进的现象,就是“死锁“。

发生死锁后若无外力干涉,这些进程都将无法向前推进。

死锁,饥饿,死循环的区别

  • 死锁:各进程互相等待对方手里的资源,导致各进程都阻塞,无法向前推进的现象。
  • 饥饿:由于长期得不到想要的资源,某进程无法向前推进的现象。比如:在短进程优先(SPF)算法中,若有源源不断的短进程到来,则长进程将一直得不到处理机,从而发生长进程“饥饿”。
  • 死循环:某进程执行过程中一直跳不出某个循环的现象。有时是因为程序逻辑bug 导致的,有时是程序员故意设计的。

image

死锁产生的必要条件

产生死锁必须同时满足一下四个条件,只要其中任一条件不成立,死锁就不会发生。

  • 互斥条件:只有对必须互斥使用的资源的争抢才会导致死锁(如哲学家的筷子、打印机设备)。像内存、扬声器这样可以同时让多个进程使用的资源是不会导致死锁的(因为进程不用阻塞等待这种资源)。

  • 不剥夺条件:进程所获得的资源在未使用完之前,不能由其他进程强行夺走,只能主动释放。

  • 请求 和 保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己已有的资源保持不放。

  • 循环等待条件:存在一种进程资源的循环等待链,链中的每一个进程已获得的资源同时被下一个进程所请求。

注意 : 发生死锁时一定有循环等待 , 但是发生循环等待时未必死锁(循环等待是死锁的必要不充分条件)

如果同类资源数大于1,则即使有循环等待,也未必发生死锁。但如果系统中每类资源都只有一个,那循环等待就是死锁的充分必要条件了。

什么时候会发生死锁

  1. 对系统资源的竞争。各进程对不可剥夺的资源(如打印机)的竞争可能引起死锁,对可剥夺的资源(CPU)的竞争是不会引起死锁的

  2. 进程推进顺序非法。请求和释放资源的顺序不当,也同样会导致死锁。例如,并发执行的进程P1、P2分别申请并占有了资源R1、R2,之后进程p1又紧接着申请资源R2,而进程p2又申请资源R1,两者会因为申请的资源被对方占有而阻塞,从而发生死锁。

  3. 信号量的使用不当也会造成死锁。如生产者-消费者问题中,如果实现互斥的P操作在实现同步的P操作之前,就有可能导致死锁。(可以把互斥信号量、同步信号量也看做是一种抽象的系统资源)

总之,对不可剥夺资源的不合理分配,可能导致死锁。

死锁的处理策略

  1. 预防死锁。破坏死锁产生的四个必要条件中的一个或几个。
  2. 避免死锁。用某种方法防止系统进入不安全状态,从而避免死锁(银行家算法)
  3. 死锁的检测和解除。允许死锁的发生,不过操作系统会负责检测出死锁的发生,然后采取某种措施解除死锁。

预防死锁

image

避免死锁

什么是安全序列

image

image

  • 所谓安全序列,就是指如果系统按照这种序列分配资源,则每个进程都能顺利完成。只要能找出一个安全序列,系统就是安全状态。当然,安全序列可能有多个。
  • 如果分配了资源之后,系统中找不出任何一个安全序列,系统就进入了不安全状态。这就意味着之后可能所有进程都无法顺利的执行下去。当然,如果有进程提前归还了一些资源,那系统也有可能重新回到安全状态,不过我们在分配资源之前总是要考虑到最坏的情况。
  • 如果系统处于安全状态,就一定不会发生死锁。如果系统进入不安全状态,则可能会发生死锁。(不安全状态未必就是发生了死锁,但发生死锁时一定是在不安全状态)
  • 因此可以在资源分配之前预先判断这次分配是否会导致系统进入不安全状态,以此决定是否答应资源分配请求。这也是“银行家算法”的核心思想。

银行家算法

数据结构:

  • 长度为m的一维数组 Available表示还有多少可用资源
  • n*m矩阵Max表示各进程对资源的最大需求数
  • n*m矩阵Allocation表示已经给各进程分配了多少资源
  • Max - Allocation = Need矩阵表示各进程最多还需要多少资源
  • 用长度为m的一位数组Request表示进程此次申请的各种资源数

银行家算法步骤:

  1. 检查此次申请是否超过了之前声明的最大需求数
  2. 检查此时系统剩余的可用资源是否还能满足这次请求
  3. 试探着分配,更改各数据结构
  4. 用安全性算法检查此次分配是否会导致系统进入不安全状态

安全性算法步骤:
检查当前的剩余可用资源是否能满足某个进程的最大需求,如果可以,就把该进程加入安全序列,并把该进程持有的资源全部回收。
不断重复上述过程,看最终是否能让所有进程都加入安全序列。

死锁的检测和解除

死锁的检测

为了能对系统是否已发生了死锁进行检测,必须:

①用某种数据结构来保存资源的请求和分配信息;
②提供一种算法,利用上述信息来检测系统是否已进入死锁状态。

image

如果系统中剩余的可用资源数足够满足进程的需求,那么这个进程暂时是不会阻塞的,可以顺利地执行下去。如果这个进程执行结束了把资源归还系统,就可能使某些正在等待资源的进程被激活,并顺利地执行下去。相应的,这些被激活的进程执行完了之后又会归还一些资源,这样可能又会激活另外一些阻塞的进程.
如果按上述过程分析,最终能消除所有边,就称这个图是可完全简化的。此时一定没有发生死锁(相当于能找到一个安全序列)。
如果最终不能消除所有边,那么此时就是发生了死锁。

死锁定理:如果某时刻系统的资源分配图是不可完全简化的,那么此时系统死锁。

死锁的解除

一旦检测出死锁的发生,就应该立即解除死锁。
补充:并不是系统中所有的进程都是死锁状态,用死锁检测算法化简资源分配图后,还连着边的那些进程就是死锁进程
解除死锁的主要方法有 :

  1. 资源剥夺法。挂起(暂时放到外存上)某些死锁进程,并抢占它的资源,将这些资源分配给其他的死锁进程。但是应防止被挂起的进程长时间得不到资源而饥饿。
  2. 撤销进程法(或称终止进程法)。强制撤销部分、甚至全部死锁进程,并剥夺这些进程的资源。这种方式的优点是实现简单,但所付出的代价可能会很大。因为有些进程可能已经运行了很长时间,已经接近结束了,一旦被终止可谓功亏一篑,以后还得从头再来
  3. 进程回退法。让一个或多个死锁进程回退到足以避免死锁的地步。这就要求系统要记录进程的历史信息,设置还原点。

如何选择对哪些进程动手?

  1. 进程优先级 (优先级低的)
  2. 已执行多长时间 (执行时间短的)
  3. 还要多久能完成 (时间长的进行处理)
  4. 进程己经使用了多少资源 (资源多的)
  5. 进程是交互式的还是批处理式的 (进行批处理的)

内存管理

基础知识

内存是用于存放数据的硬件。程序执行前需要先放到内存中才能被CPU处理。

image

相对地址和绝对地址

编译时产生的指令只关心“相对地址”,实际放入内存中时再想办法根据起始位置得到“绝对地址”。
Eg: 编译时只需确定变量x存放的相对地址是100(也就是说相对于进程在内存中的起始地址而言的地址)。CPU 想要找到x在内存中的实际存放位置,只需要用进程的起始地址+100即可。

相对地址又称逻辑地址,绝对地址又称物理地址。

写程序到程序运行

image

内存管理

操作系统对内存进行管理,那么要管理哪些内容呢?

  1. 内存空间的分配和回收
  2. 操作系统需要提供某种技术从逻辑上对内存空间进行扩充。
  3. 操作系统需要提供地址转换功能,负责程序的逻辑地址与物理地址的转换

image

  1. 操作系统需要提供内存保护功能。保证各进程在各自存储空间内运行,互不干扰

内存保护可采取两种方法:

方法一:在CPU中设置一对上、下限寄存器,存放进程的上、下限地址。进程的指令要访问某个地址时,CPU检查是否越界。

image

方法二:采用重定位寄存器(又称基址寄存器)和界地址寄存器(又称限长寄存器)进行越界检查。重定位寄存器中存放的是进程的起始物理地址。界地址寄存器中存放的是进程的最大逻辑地址。

image

覆盖和交换

image

覆盖技术

覆盖技术的思想 : 将程序分为多个段(多个模块)。常用的段常驻内存,不常用的段在需要时调入内存。
内存中分为一个“固定区”和若干个“覆盖区”。需要常驻内存的段放在“固定区”中,调入后就不再调出(除非运行结束)不常用的段放在“覆盖区”,需要用到时调入内存,用不到时调出内存。

image

image

必须由程序员声明覆盖结构,操作系统完成自动覆盖。缺点:对用户不透明,增加了用户编程负担。覆盖技术只用于早期的操作系统中,现在已成为历史。

交换技术

交换(对换)技术的设计思想: 内存空间紧张时,系统将内存中某些进程暂时换出外存,把外存中某些已具备运行条件的进程换入内存(进程在内存与磁盘间动态调度)。之前讲的中级调度(内存调度)就是为这个服务的。

image

1.应该在外存(磁盘)的什么位置保存被换出的进程?

具有对换功能的操作系统中,通常把磁盘空间分为文件区和对换区两部分。文件区主要用于存放文件,主要追求存储空间的利用率,因此对文件区空间的管理采用离散分配方式;对换区空间只占磁盘空间的小部分,被换出的进程数据就存放在对换区。由于对换的速度直接影响到系统的整体速度,因此对换区空间的管理主要追求换入换出速度,因此通常对换区采用连续分配方式(学过文件管理章节后即可理解)。总之,对换区的I/O速度比文件区的更快。

2.什么时候应该交换?

交换通常在许多进程运行且内存吃紧时进行,而系统负荷降低就暂停。例如:在发现许多进程运行时经常发生缺页,就说明内存紧张,此时可以换出一些进程;如果缺页率明显下降,就可以暂停换出。

3.应该换出哪些进程?

可优先换出阻塞进程;可换出优先级低的进程;为了防止优先级低的进程在被调入内存后很快又被换出,有的系统还会考虑进程在内存的驻留时间…

(注意:PCB会常驻内存,不会被换出外存)

连续分配管理方式

连续分配:指为用户进程分配的必须是一个连续的内存空间。

image

单一连续分配

  • 在单一连续分配方式中,内存被分为系统区和用户区。系统区通常位于内存的低地址部分,用于存放操作系统相关数据;用户区用于存放用户进程相关数据。
  • 内存中只能有一道用户程序,用户程序独占整个用户区空间。
  • 优点: 实现简单 ;无外部碎片;可以采用覆盖技术扩充内存;不一定需要采取内存保护(eg:早期的 PC操作系统MS-DOS)。
  • 缺点:只能用于单用户、单任务的操作系统中;有内部碎片;存储器利用率极低。

image

固定分区分配

image

image

动态分区分配

动态分区分配又称为可变分区分配。这种分配方式不会预先划分内存分区,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。因此系统分区的大小和数目是可变的。

  • 使用这种方式的话,我们需要考虑以下三个问题。
  1. 系统要用什么样的数据结构记录内存的使用情况?

image

  1. 当很多个空闲分区都能满足需求时,应该选择哪个分区进行分配?

    使用动态分区算法,这个将在下一小节进行详细介绍。
    
  2. 如何进行分区的分配与回收操作?

  • 如何分配 ———–> 使用动态分区算法之后,修改数据结构即可。

image

  • 如何回收——————————-> 牢记一点即可,会把相邻的空闲区域合并为一个。

image

内部碎片和外部碎片

  • 动态分区分配没有内部碎片,但是有外部碎片。
  • 内部碎片,分配给某进程的内存区域中,如果有些部分没有用上。
  • 外部碎片,是指内存中的某些空闲分区由于太小而难以利用。
  • 如果内存中空闲空间的总和本来可以满足某进程的要求,但由于进程需要的是一整块连续的内存空间,因此这些
    进程“碎片”不能满足进程的需求。可以通过紧凑((拼凑,Compaction)技术来解决外部碎片。

动态分区分配算法

动态分区分配算法:在动态分区分配方式中,当很多个空闲分区都能满足需求时,应该选择哪个分区进行分配?

首次适应算法

算法思想: 每次都从低地址开始查找,找到第一个能满足大小的空闲分区
如何实现:空闲分区以地址递增的次序排列。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。

image

最佳适应算法

算法思想:由于动态分区分配是一种连续分配方式,为各进程分配的空间必须是连续的一整片区域。因此为了保证当“大进程”到来时能有连续的大片空间,可以尽可能多地留下大片的空闲区,即,优先使用更小的空闲区
如何实现:空闲分区按容量递增次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。

image

最大适应算法

又称最坏适应算法(Largest Fit)
算法思想:为了解决最佳适应算法的问题――即留下太多难以利用的小碎片,可以在每次分配时优先使用最大的连续空闲区,这样分配后剩余的空闲区就不会太小,更方便使用。
如何实现:空闲分区按容量递减次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。

image

临近适应算法

算法思想:首次适应算法每次都从链头开始查找的。这可能会导致低地址部分出现很多小的空闲分区,而每次分配查找时,都要经过这些分区,因此也增加了查找的开销。如果每次都从上次查找结束的位置开始检索,就能解决上述问题。
如何实现:空闲分区以地址递增的顺序排列(可排成一个循环链表)。每次分配内存时从上次查找结束的位置开始查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。

image

四种算法对比

image

基本分页存储管理

连续分配:为用户进程分配的必须是一个连续的内存空间。

非连续分配:为用户进程分配的可以是一些分散的内存空间。

image

基本分页存储管理的思想――把内存分为一个个相等的小分区,再按照分区大小把进程拆分成一个个小部分。

将内存空间分为一个个大小相等的分区(比如:每个分区4KB),每个分区就是一个“页框”,或称“页帧”、“内存块”、“物理块”。每个页框有一个编号,即“页框号”(或者“内存块号”、“页帧号”、“物理块号”)页框号从0开始。

将用户进程的地址空间也分为与页框大小相等的一个个区域,称为“页”或“页面”。每个页面也有一个编号,即“页号”,页号也是从0开始。
(注:进程的最后一个页面可能没有一个页框那么大。因此,页框不能太大,否则可能产生过大的内部碎片)

操作系统以页框为单位为各个进程分配内存空间。进程的每个页面分别放入一个页框中。也就是说,进程的页面与内存的页框有一一对应的关系。

各个页面不必连续存放,也不必按先后顺序来,可以放到不相邻的各个页框中。

思考: 将进程地址空间分页之后,操作系统该如何实现逻辑地址到物理地址的转换?

  1. 要算出逻辑地址对应的页号
  2. 要知道该页号对应页面在内存中的起始地址
  3. 要算出逻辑地址在页面内的“偏移量”
  4. 物理地址 = 页面始址+页内偏移量

如何计算:

  1. 页号=逻辑地址/页面长度(取除法的整数部分)
  2. 页内偏移量 = 逻辑地址%页面长度(取除法的余数部分)
  3. 页面在内存中的起始位置:操作系统需要用某种数据结构记录进程各个页面的起始位置。

举例:

页号=80 / 50= 1
页内偏移量=80 % 50 = 30
1号页在内存中存放的起始位置450

image

思考: 如何知道该页号对应页面在内存中的起始地址?

操作系统为每一个进程创建一个页表?

image

  • 如何理解每个页表项的长度是相同的,页号是“隐含的”?

image

基本地址变换机构

基本地址变换机构可以借助进程的页表将逻辑地址转换为物理地址
通常会在系统中设置一个页表寄存器(PTR),存放页表在内存中的起始地址F和页表长度M。进程未执行时,页表的始址和页表长度放在进程控制块(PCB)中,当进程被调度时,操作系统内核会把它们放到页表寄存器中。

  • 执行流程

image

  • 页表项长度,页表长度,页面大小

页表长度是指这个页表中总共有几个页表项,即总共有多少页。页面大小是指一个页面占多大的存储空间。页表项长度是指每个页表项占多大的存储空间。

通过下面这个例子来理解页表项长度。

Eg:假设某系统物理内存大小为4GB,页面大小为4KB,内存总共会被分为2^32/ 2^12=2^20个内存块,因此内存块号的范围应该是0~2^20 - 1。因此至少要20个二进制位才能表示这么多的内存块号,因此至少要3个字节才够(每个字节8个二进制位,3个字节共24个二进制位)。每个块号用三个字节来表示。

image

具有快表的地址变换机构

局部性原理

时间局部性:如果执行了程序中的某条指令,那么不久后这条指令很有可能再次执行;如果某个数据被访问过,不久之后该数据很可能再次被访问。(因为程序中存在大量的循环)
空间局部性:一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也很有可能被访问。(因为很多数据在内存中都是连续存放的)

上小节介绍的基本地址变换机构中,每次要访问一个逻辑地址,都需要查询内存中的页表。由于局部性原理,可能连续很多次查到的都是同一个页表项。既然如此,能否利用这个特性减少访问页表的次数呢?

快表

快表,又称联想寄存器(TLB),是一种访问速度比内存快很多的高速缓冲存储器,用来存放当前访问的若干页表项,以加速地址变换的过程。与此对应,内存中的页表常称为慢表。

  • 执行流程

image

两级页表

两级页表的出现主要是为了解决单级页表的问题。那么单级页表有什么问题呢?

问题一:页表必须连续存放,因此当页表很大时,需要占用很多个连续的页框。
问题二:没有必要让整个页表常驻内存,因为进程在一段时间内可能只需要访问某几个特定的页面。

1.解决问题一

我们可以回想以下当初是如何解决进程必须连续的问题 ?

我们可以把页表放在不同的页框中,再用一个表来记录各个各个子页表所在位置,我们把这个表叫做页目录表(外层页表,顶级页表)。

image

2.解决问题二

image

其他细节

  1. 若采用多级页表机制,则各级页表的大小不能超过一个页面

  2. 两级页表的访存次数分析(假设没有快表机构)

  • 第一次访存:访问内存中的页目录表
  • 第二次访存:访问内存中的二级页表
  • 第三次访存:访问目标内存单元

==N级页表访问一个逻辑地址需要N+1次访问内存。==

基本分段存储管理方式

分段

​ 进程的地址空间:按照程序自身的逻辑关系划分为若干个段,每个段都有一个段名(在低级语言中,程序员使用段名来编程),每段从0开始编址。
内存分配规则 : 以段为单位进行分配,每个段在内存中占据连续空间,但各段之间可以不相邻。

image

  • 分段系统的逻辑地址结构由段号(段名)和段内地址(段内偏移量)所组成。

image

段号的位数决定了每个进程最多可以分几个段。

段内地址位数决定了每个段的最大长度是多少。

段表

image

段内寻址

image

分段,分页对比

  • 页是信息的物理单位。分页的主要目的是为了实现离散分配,提高内存利用率。分页仅仅是系统管理上的需要,完全是系统行为,对用户是不可见的。
  • 段是信息的逻辑单位。分段的主要目的是更好地满足用户需求。一个段通常包含着一组属于一个逻辑模块的信息。分段对用户是可见的,用户编程时需要显式地给出段名。
  • 页的大小固定且由系统决定。段的长度却不固定,决定于用户编写的程序。
  • 分页的用户进程地址空间是一维的,程序员只需给出一个记忆符即可表示一个地址。
  • 分段的用户进程地址空间是二维的,程序员在标识一个地址时,既要给出段名,也要给出段内地址。
  • 分段比分页更容易实现信息的共享和保护。

段页式管理方式

分页,分段的优缺点

image

既然两者都有优缺点,那么可不可以把他们结合起来呢?答案当然是可以的。如下图所示。

image

段页式管理的逻辑结构

image

段号的位数决定了每个进程最多可以分几个段

页号位数决定了每个段最大有多少页

页内偏移量决定了页面大小、内存块大小是多少

段内寻址

image

虚拟内存

传统存储管理方式的特征和缺点

  • 一次性:作业必须一次性全部装入内存后才能开始运行。这会造成两个问题:①作业很大时,不能全部装入内存,导致大作业无法运行;②当大量作业要求运行时,由于内存无法容纳所有作业,因此只有少量作业能运行,导致多道程序并发度下降。
  • 驻留性:一旦作业被装入内存,就会一直驻留在内存中,直至作业运行结束。事实上,在一个时间段内,只需要访问作业的一小部分数据即可正常运行,这就导致了内存中会驻留大量的、暂时用不到的数据,浪费了宝贵的内存资源。

image

虚拟内存的定义和特征

  • 基于局部性原理(忘记的话,可以到第8节查看),在程序装入时,可以将程序中很快会用到的部分装入内存,暂时用不到的部分留在外存,就可以让程序开始执行。
  • 在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序。
  • 若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存。
  • 在操作系统的管理下,在用户看来似乎有一个比实际内存大得多的内存,这就是虚拟内存。

易混知识点:

虚拟内存的最大容量是由计算机的地址结构( CPU寻址范围)确定的

虚拟内存的实际容量= min(内存和外存容量之和,CPU寻址范围)

如: 某计算机地址结构为32位,按字节编址,内存大小为512MB,外存大小为2GB.

则虚拟内存的最大容量为2^32B= 4GB 。 虚拟内存的实际容量=min (2^32B,512MB+2GB)= 2GB+512MB

虚拟内存有一下三个主要特征:

  • 多次性:无需在作业运行时一次性全部装入内存,而是允许被分成多次调入内存。
  • 对换性:在作业运行时无需一直常驻内存,而是允许在作业运行过程中,将作业换入、换出。
  • 虚拟性:从逻辑上扩充了内存的容量,使用户看到的内存容量,远大于实际的容量。

虚拟内存的实现需要建立在离散分配的内存管理方式基础上。

image

请求分页管理方式

页表机制

与基本分页管理相比,请求分页管理中,为了实现“请求调页”,

  1. 操作系统需要知道每个页面是否已经调入内存;
  2. 如果还没调入,那么也需要知道该页面在外存中存放的位置。
  3. 当内存空间不够时,要实现“页面置换”,操作系统需要通过某些指标来决定到底换出哪个页面;
  4. 有的页面没有被修改过,就不用再浪费时间写回外存。有的页面修改过,就需要将外存中的旧数据覆盖,因此,操作系统也需要记录各个页面是否被修改的信息。

因此页表会增加四个字段来上面的信息。

image

缺页中断机制

假设此时要访问逻辑地址 = (页号,页内偏移量)= (0,1024)

在请求分页系统中,每当要访问的页面不在内存时,便产生一个缺页中断,然后由操作系统的缺页中断处理程序处理中断。

此时缺页的进程阻塞,放入阻塞队列,调页完成后再将其唤醒,放回就绪队列。

如果内存中有空闲块,则为进程分配一个空闲块,将所缺页面装入该块,并修改页表中相应的页表项。

如果内存中没有空闲块,则由页面置换算法选择一个页面淘汰,若该页面在内存期间被修改过,则要将其写回外存。未修改过的页面不用写回外存。

缺页中断是因为当前执行的指令想要访问的目标页面未调入内存而产生的,因此属于内中断

一条指令在执行期间,可能产生多次缺页中断。(如: copy AtoB,即将逻辑地址A中的数据复制到逻辑地址B,而A、B属于不同的页面,则有可能产生两次中断)

image

地址变换

image

补充知识点

①只有“写指令”才需要修改“修改位”。并且,一般来说只需修改快表中的数据,只有要将快表项删除时才需要写回内存中的慢表。这样可以减少访存次数。

②和普通的中断处理一样,缺页中断处理依然需要保留CPU现场。

③需要用某种“页面置换算法”来决定一个换出页面(下节内容)

④换入/换出页面都需要启动慢速的I/o操作,可见,如果换入/换出太频繁,会有很大的开销。

⑤页面调入内存后,需要修改慢表,同时也需要将表项复制到快表中。

页面置换算法

最佳置换算法(OPT)

最佳置换算法(OPT):每次选择淘汰的页面将是以后永不使用,或者在最长时间内不再被访问的页面,这样可以保证最低的缺页率。

最佳置换算法可以保证最低的缺页率,但实际上,只有在进程执行的过程中才能知道接下来会访问到的是哪个页面。操作系统无法提前预判页面访问序列。因此,最佳置换算法是无法实现的

image

先进先出置换算法(FIFO)

先进先出置换算法(FIFO):每次选择淘汰的页面是最早进入内存的页面

实现方法:把调入内存的页面根据调入的先后顺序排成一个队列,需要换出页面时选择队头页面即可。队列的最大长度取决于系统为进程分配了多少个内存块。

Belady异常―一当为进程分配的物理块数增大时,缺页次数不减反增的异常现象。

只有FIFO算法会产生Belady异常。另外,FIFO算法虽然实现简单,但是该算法与进程实际运行时的规律不适应,因为先进入的页面也有可能最经常被访问。因此,算法性能差。

image

最近最久未使用算法(LRU)

最近最久未使用置换算法(LRU):每次淘汰的页面是最近最久未使用的页面

实现方法:赋予每个页面对应的页表项中,用访问字段记录该页面自上次被访问以来所经历的时间t。当需要淘汰一个页面时,选择现有页面中t值最大的,即最近最久未使用的页面。

image

时钟置换算法(NRU)

最佳置换算法性能最好,但无法实现;先进先出置换算法实现简单,但算法性能差;最近最久未使用置换算法性能好,是最接近OPT算法性能的,但是实现起来需要专门的硬件支持,算法开销大。

时钟置换算法是一种性能和开销较均衡的算法,又称CLOCK算法,或最近未用算法(NRU,NotRecently Used)

简单的CLOCK 算法实现方法:为每个页面设置一个访问位,再将内存中的页面都通过链接指针链接成一个循环队列。当某页被访问时,其访问位置为1。当需要淘汰一个页面时,只需检查页的访问位。如果是0,就选择该页换出;如果是1,则将它置为0,暂不换出,继续检查下一个页面,若第一轮扫描中所有页面都是1,则将这些页面的访问位依次置为0后,再进行第二轮扫描(第二轮扫描中一定会有访问位为0的页面,因此简单的CLOCK算法选择一个淘汰页面最多会经过两轮扫描)

image

改进型的时钟置换算法

简单的时钟置换算法仅考虑到一个页面最近是否被访问过。事实上,如果被淘汰的页面没有被修改过,就不需要执行I/o操作写回外存。只有被淘汰的页面被修改过时,才需要写回外存。
因此,除了考虑一个页面最近有没有被访问过之外,操作系统还应考虑页面有没有被修改过。在其他条件都相同时,应优先淘汰没有修改过的页面,避免I/o操作。这就是改进型的时钟置换算法的思想。修改位=0,表示页面没有被修改过;修改位=1,表示页面被修改过。
为方便讨论,用(访问位,修改位)的形式表示各页面状态。如(1,1)表示一个页面近期被访问过,且被修改过。

算法规则: 将所有可能被置换的页面排成一个循环队列
第一轮:从当前位置开始扫描到第一个(0,0)的帧用于替换。本轮扫描不修改任何标志位
第二轮:若第一轮扫描失败,则重新扫描,查找第一个(0,1)的帧用于替换。本轮将所有扫描过的帧访问位设为0
第三轮:若第二轮扫描失败,则重新扫描,查找第一个(0,0)的帧用于替换。本轮扫描不修改任何标志位
第四轮:若第三轮扫描失败,则重新扫描,查找第一个(0,1)的帧用于替换。
由于第二轮已将所有帧的访问位设为0,因此经过第三轮、第四轮扫描一定会有一个帧被选中,因此改进型CLOCK置换算法选择一个淘汰页面最多会进行四轮扫描

image

五种算法对比

image

页面分配策略

页面分配,置换策略

  • 驻留集:指请求分页存储管理中给进程分配的物理块的集合。

  • 在采用了虚拟存储技术的系统中,驻留集大小一般小于进程的总大小。

    1. 若驻留集太小,会导致缺页频繁,系统要花大量的时间来处理缺页,实际上用于进程推进的时间很少。
    2. 驻留集太大,又会导致多道程序并发度下降,资源利用率降低。所以应该选择一个合适的驻留集大小。
  • 固定分配:操作系统为每个进程分配一组固定数目的物理块,在进程运行期间大小不变。即,驻留集大小不变。

  • 可变分配:先为每个进程分配一定数目的物理块,在进程运行期间,可根据情况做适当的增加或减少。即,驻留集大小可变

  • 局部置换:发生缺页时只能选进程自己的物理块进行置换

  • 全局置换:可以将操作系统保留的空闲物理块分配给缺页进程,也可以将别的进程持有的物理块置换到外存,再分配给缺页进程。

image

下面来分别介绍这几种方式。

  • 固定分配局部置换:系统为每个进程分配一定数量的物理块,在整个运行期间都不改变。若进程在运行中发生缺页,则只能从该进程在内存中的页面中选出一页换出,然后再调入需要的页面。这种策略的缺点是:很难在刚开始就确定应为每个进程分配多少个物理块才算合理。(采用这种策略的系统可以根据进程大小、优先级、或是根据程序员给出的参数来确定为一个进程分配的内存块数)

  • 可变分配全局置换:刚开始会为每个进程分配一定数量的物理块。操作系统会保持一个空闲物理块队列。当某进程发生缺页时,从空闲物理块中取出一块分配给该进程;若已无空闲物理块,则可选择一个未锁定的页面换出外存,再将该物理块分配给缺页的进程。采用这种策略时,只要某进程发生缺页都将获的物理块,仅当空闲物理块用完时,系统才选择一个未锁定的页面调出。被选择调出的页可能是进程中任意一个进程的页,因此被选中的这个进程物理块会减少,缺页率会增加。

  • 可变分配局部置换: 刚开始会为每个进程分配一定数量的物理块,当某进程发生缺页时,只允许从该进程自己的物理块中选出一个换出外存。如果进程在运行中频繁地缺页,系统会为该进程多分配几个物理块,直至该进程缺页率趋势适当,反之,如果进程在运行中缺页率特别低,则可适当减少分配给该进程的物理块。

  • 可变分配全局置换:只要缺页就给分配新物理块

  • 可变分配局部置换:根据发生缺页的频率来动态地增加或减少进程的物理块

何时调入页面

  1. 预调页策略:根据局部性原理(主要是空间局部性),一次调入若干个相邻的页面可能比一次调入一个页面更高效。但如果提前调入的页面中大多数都没被访问过,则又是低效的。因此可以预测不久之后可能访问到的页面,将它们预先调入内存,但目前预测成功率只有50%左右。故这种策略主要用于进程的首次调入,由程序员指出应该先调入哪些部分。它是运行前调入

  2. 请求调页策略:进程在运行期间发现缺页时才将所缺页面调入内存。由这种策略调入的页面一定会被访问到,但由于每次只能调入一页,而每次调页都要磁盘l/o操作,因此I/o开销较大。它是运行时调入

从何处调入页面

  1. 系统拥有足够的对换区空间:页面的调入、调出都是在内存与对换区之间进行,这样可以保证页面的调入、调出速度很快。在进程运行前,需将进程相关的数据从文件区复制到对换区。

image

  1. 系统缺少足够的对换区空间:凡是不会被修改的数据都直接从文件区调入,由于这些页面不会被修改,因此换出时不必写回磁盘,下次需要时再从文件区调入即可。对于可能被修改的部分,换出时需写回磁盘对换区,下次需要时再从对换区调入。

image

  1. UNIX方式:运行之前进程有关的数据全部放在文件区,故未使用过的页面,都可从文件区调入。若被使用过的页面需要换出,则写回对换区,下次需要时从对换区调入。

image

抖动现象,工作集

  • 抖动:刚刚换出的页面马上又要换入内存,刚刚换入的页面马上又要换出外存,这种频繁的页面调度行为称为抖动,或颠簸。
  • 产生抖动的主要原因是进程频繁访问的页面数目高于可用的物理块数(分配给进程的物理块不够)。
  • 为进程分配的物理块太少,会使进程发生抖动现象。为进程分配的物理块太多,又会降低系统整体的并发度,降低某些资源的利用率
  • 为了研究为应该为每个进程分配多少个物理块,Denning提出了进程“工作集”的概念。
  • 驻留集:指请求分页存储管理中给进程分配的内存块的集合。
  • 工作集:指在某段时间间隔里,进程实际访问页面的集合。

一般来说,驻留集大小不能小于工作集大小,否则进程运行过程中将频繁缺页。

image

文件系统

文件管理

  • 文件――就是一组有意义的信息/数据集合。

  • 一个文件有哪些属性?

    1. 文件名:由创建文件的用户决定文件名,主要是为了方便用户找到文件,同一目录下不允许有重名文件。
    2. 标识符:一个系统内的各文件标识符唯一,对用户来说毫无可读性,因此标识符只是操作系统用于区分各个文件的一种内部名称。
    3. 类型:指明文件的类型
    4. 位置:文件存放的路径(让用户使用)、在外存中的地址(操作系统使用,对用户不可见)
    5. 大小:指明文件大小创建时间、上次修改时间文件所有者信息
    6. 保护信息:对文件进行保护的访问控制信息
  • 文件分为有结构文件和无结构文件。

image

  • 操作系统向上(用户和应用程序)提供的功能

image

文件的逻辑结构

image

按文件是否有结构分类,可以分为无结构文件、有结构文件两种。

无结构文件:文件内部的数据就是一系列二进制流或字符流组成。又称“流式文件”。如:Windows操作系统中的.txt文件。

有结构文件:由一组相似的记录组成,又称“记录式文件”。每条记录又若干个数据项组成。如:数据库表文件。一般来说,每条记录有一个数据项可作为关键字(作为识别不同记录的ID)

我们主要研究有结构文件。

image

顺序文件

顺序文件:文件中的记录一个接一个地顺序排列(逻辑上),记录可以是定长的或可变长的。各个记录在物理上可以顺序存储(相当于数组)或链式存储(相当于链表)。

顺序存储又可以分为串结构和顺序结构。

image

那么这几种存储方式可以快速找到第i个记录对应的地址呢?

image

结论:定长记录的顺序文件,若物理上采用顺序存储,则可实现随机存取;若能再保证记录的顺序结构,则可实现快速检索(即根据关键字快速找到对应记录)

索引文件

思考:对于可变长记录文件,要找到第i个记录,必须先顺序第查找前i-1个记录,但是很多应用场景中又必须使用可变长记录。如何解决这个问题?

这时我们可以建立一张索引表来快速找到第i个记录。如图所示:

image

索引表本身是定长记录的顺序文件。因此可以快速找到第i个记录对应的索引项。

可将关键字作为索引号内容,若按关键字顺序排列,则还可以支持按照关键字折半查找。

每当要增加/删除一个记录时,需要对索引表进行修改。由于索引文件有很快的检索速度,因此主要用于对信息处理的及时性要求比较高的场合。

另外,可以用不同的数据项建立多个索引表。如:学生信息表中,可用关键字“学号”建立一张索引表。也可用“姓名”建立一张索引表。这样就可以根据“姓名”快速地检索文件了。(Eg: SQL就支持根据某个数据项建立索引的功能)

索引顺序文件

思考索引文件的缺点:每个记录对应一个索引表项,因此索引表可能会很大。比如:文件的每个记录平均只占8字节,而每个索引表项占32个字节,那么索引表都要比文件内容本身大4倍,这样对存储空间的利用率就太低了。

那么如何解决呢?

我们可以建立一个索引顺序文件。

索引顺序文件是索引文件和顺序文件思想的结合。索引顺序文件中,同样会为文件建立一张索引表,但不同的是:并不是每个记录对应一个索引表项,而是一组记录对应一个索引表项。

在本例中,学生记录按照学生姓名的开头字母进行分组。每个分组就是一个顺序文件,分组内的记录不需要按关键字排序。

image

多级索引顺序文件

为了进一步提高检索效率,可以为顺序文件建立多级索引表。

例如,对于一个含10^6个记录的文件,可先为该文件建立一张低级索引表,每100个记录为一组,故低级索引表中共有10000个表项(即10000个定长记录),再把这10000个定长记录分组,每组100个,为其建立顶级索引表,故顶级索引表中共有100个表项。

image

文件目录

image

  • 目录本身就是一种有结构文件,由一条条记录组成。每条记录对应一个在该放在该目录下的文件。如图所示

image

文件控制块(FCB)

  • 目录文件中的一条记录就是一个“文件控制块(FCB)

FCB的有序集合称为“文件目录”,一个FCB就是一个文件目录项。

FCB中包含了文件的基本信息(文件名、物理地址、逻辑结构、物理结构等),存取控制信息(是否可读/可写、禁止访问的用户名单等),使用信息(如文件的建立时间、修改时间等)。

最重要,最基本的还是文件名、文件存放的物理地址。

单级目录结构

image

二级目录结构

image

多级目录结构(树形目录结构)

image

树形目录结构可以很方便地对文件进行分类,层次结构清晰,也能够更有效地进行文件的管理和保护。但是,树形结构不便于实现文件的共享。为此,提出了“无环图目录结构”。

无环图目录结构

image

可以用不同的文件名指向同一个文件,甚至可以指向同一个目录(共享同一目录下的所有内容)。

需要为每个共享结点设置一个共享计数器,用于记录此时有多少个地方在共享该结点。用户提出删除结点的请求时,只是删除该用户的FCB、并使共享计数器减1,并不会直接删除共享结点。

只有共享计数器减为0时,才删除结点。

注意:共享文件不同于复制文件。在共享文件中,由于各用户指向的是同一个文件,因此只要其中一个用户修改了文件数据,那么所有用户都可以看到文件数据的变化。

索引结点(对FCB的改进)

image

当找到文件名对应的目录项时,才需要将索引结点调入内存,索引结点中记录了文件的各种信息,包括文件在外存中的存放位置,根据“存放位置”即可找到文件。

存放在外存中的索引结点称为“磁盘索引结点”,当索引结点放入内存后称为“内存索引结点”。相比之下内存索引结点中需要增加一些信息,比如:文件是否被修改、此时有几个进程正在访问该文件等。

文件分配方式(文件物理结构)

image

在介绍这些分配方式之前,先介绍一下什么是文件块,磁盘块。

在内存管理中,进程的逻辑地址空间被分为一个一个页面。

同样的,在外存管理中,为了方便对文件数据的管理,文件的逻辑地址空间也被分为了一个一个的文件“块”。

于是文件的逻辑地址也可以表示为(逻辑块号,块内地址)的形式。

用户通过逻辑地址来操作自己的文件,操作系统要负责实现从逻辑地址到物理地址的映射。

image

连续分配

连续分配方式要求每个文件在磁盘上占有一组连续的块。如图所示

用户给出要访问的逻辑块号,操作系统找到该文件对应的目录项(FCB)

物理块号=起始块号+逻辑块号

当然,还需要检查用户提供的逻辑块号是否合法(逻辑块号≥长度就不合法)、

优点:支持顺序访问和直接访问(即随机访问);连续分配的文件在顺序访问时速度最快

缺点:不方便文件拓展;存储空间利用率低,会产生磁盘碎片

image

链接分配——隐式链接

链接分配采取离散分配的方式,可以为文件分配离散的磁盘块。分为隐式链接和显式链接两种。

  • 从逻辑块号到物理块号的转变

用户给出要访问的逻辑块号i,操作系统找到该文件对应的目录项(FCB)

从目录项中找到起始块号(即0号块),将0号逻辑块读入内存,由此知道1号逻辑块存放的物理块号,于是读入1号逻辑块,再找到2号逻辑块的存放位置……以此类推。

因此,读入i号逻辑块,总共需要i+1次磁盘l/O。

结论:采用链式分配(隐式链接)方式的文件,只支持顺序访问,不支持随机访问,查找效率低。另外,指向下一个盘块的指针也需要耗费少量的存储空间。

image

链接分配——显式链接

把用于链接文件各物理块的指针显式地存放在一张表中。即文件分配表(FAT,File Allocation Table)。如图所示

注意:一个磁盘仅设置一张FAT。开机时,将FAT读入内存,并常驻内存。FAT的各个表项在物理上连续存储,且每一个表项长度相同,因此“物理块号”字段可以是隐含的。

  • 从逻辑块号到物理块号的转变

用户给出要访问的逻辑块号i,操作系统找到该文件对应的目录项( FCB)

从目录项中找到起始块号,若i>0,则查询内存中的文件分配表FAT,往后找到i号逻辑块对应的物理块号。逻辑块号转换成物理块号的过程不需要读磁盘操作。

结论:采用链式分配(显式链接)方式的文件,支持顺序访问,也支持随机访问(想访问i号逻辑块时,并不需要依次访问之前的 0~i-1号逻辑块),由于块号转换的过程不需要访问磁盘,因此相比于隐式链接来说,访问速度快很多。

显然,显式链接也不会产生外部碎片,也可以很方便地对文件进行拓展。

image

两种链接分配方式总结

隐式链接――除文件的最后一个盘块之外,每个盘块中都存有指向下一个盘块的指针。文件目录包括文件第一块的指针和最后一块的指针。

  • 优点:很方便文件拓展,不会有碎片问题,外存利用率高。
  • 缺点:只支持顺序访问,不支持随机访问,查找效率低,指向下一个盘块的指针也需要耗费少量的存储空间。

显式链接――把用于链接文件各物理块的指针显式地存放在一张表中,即文件分配表(FAT,FileAllocation Table)。一个磁盘只会建立一张文件分配表。开机时文件分配表放入内存,并常驻内存。

  • 优点:很方便文件拓展,不会有碎片问题,外存利用率高,并且支持随机访问。相比于隐式链接来说,地址转换时不需要访问磁盘,因此文件的访问效率更高。
  • 缺点:文件分配表的需要占用一定的存储空间。

索引分配

索引分配允许文件离散地分配在各个磁盘块中,系统会为每个文件建立一张索引表,索引表中记录了文件的各个逻辑块对应的物理块(索引表的功能类似于内存管理中的页表――建立逻辑页面到物理页之间的映射关系)。索引表存放的磁盘块称为索引块。文件数据存放的磁盘块称为数据块。

  • 从逻辑块号到物理块号的转变

用户给出要访问的逻辑块号i,操作系统找到该文件对应的目录项(FCB)

从目录项中可知索引表存放位置,将索引表从外存读入内存,并查找索引表即可知道i号逻辑块在外存中的存放位置。

可见,索引分配方式可以支持随机访问。文件拓展也很容易实现(只需要给文件分配一个空闲块,并增加一个索引表项即可)

但是索引表需要占用一定的存储空间

image

  • 如果一个文件的索引表太大,一个磁盘块放不下,那么如何解决呢?

可以用以下三种方式解决。

①链接方案:如果索引表太大,一个索引块装不下,那么可以将多个索引块链接起来存放。

image

②多层索引:建立多层索引(原理类似于多级页表)。使第一层索引块指向第二层的索引块。还可根据文件大小的要求再建立第三层、第四层索引块。

image

③混合索引:多种索引分配方式的结合。例如,一个文件的顶级索引表中,既包含直接地址索引(直接指向数据块),又包含一级间接索引(指向单层索引表)、还包含两级间接索引(指向两层索引表)。

image

总结

①链接方案 : 如果索引表太大,一个索引块装不下,那么可以将多个索引块链接起来存放。缺点:若文件很大,索引表很长,就需要将很多个索引块链接起来。想要找到i号索引块,必须先依次读入0~i-1号索引块,这就导致磁盘I/O次数过多,查找效率低下。

②多层索引:建立多层索引(原理类似于多级页表)。使第一层索引块指向第二层的索引块。还可根据文件大小的要求再建立第三层、第四层索引块。采用K层索引结构,且顶级索引表未调入内存,则访问一个数据块只需要K+1次读磁盘操作。缺点:即使是小文件,访问一个数据块依然需要K+1次读磁盘。

③混合索引:多种索引分配方式的结合。例如,一个文件的顶级索引表中,既包含直接地址索引(直接指向数据块),又包含一级间接索引(指向单层索引表)、还包含两级间接索引(指向两层索引表)。优点:对于小文件来说,访问一个数据块所需的读磁盘次数更少。

三种分配方式总结

image

对空闲磁盘块的管理(文件存储空间管理)

image

文件卷

存储空间的划分:将物理磁盘划分为一个个文件卷(逻辑卷、逻辑盘)。所谓的文件卷就相当于电脑上的C盘,D盘等。

image

空闲表法

为一个磁盘创建一个表,来存储空闲磁盘块的位置。

如何分配磁盘块 : 与内存管理中的动态分区分配很类似,为一个文件分配连续的存储空间。同样可采用首次适应、最佳适应、最坏适应等算法来决定要为文件分配哪个区间。

如何回收磁盘块:与内存管理中的动态分区分配很类似,当回收某个存储区时需要有四种情况―—①回收区的前后都没有相邻空闲区;②回收区的前后都是空闲区;③回收区前面是空闲区;④回收区后面是空闲区。总之,回收时需要注意表项的合并问题。

image

空闲链表法

空闲链表发分为空闲盘块链和空闲盘区链。

image

空闲盘块链

  • 操作系统保存着链头、链尾指针。
  • 如何分配:若某文件申请K个盘块,则从链头开始依次摘下K个盘块分配,并修改空闲链的链头指针。
  • 如何回收:回收的盘块依次挂到链尾,并修改空闲链的链尾指针。
  • 适用于离散分配的物理结构。为文件分配多个盘块时可能要重复多次操作。

image

空闲盘区链

  • 操作系统保存着链头、链尾指针。
  • 如何分配:若某文件申请K个盘块,则可以采用首次适应、最佳适应等算法,从链头开始检索,按照算法规则找到一个大小符合要求的空闲盘区,分配给文件。若没有合适的连续空闲块,也可以将不同盘区的盘块同时分配给一个文件,注意分配后可能要修改相应的链指针、盘区大小等数据。
  • 如何回收:若回收区和某个空闲盘区相邻,则需要将回收区合并到空闲盘区中。若回收区没有和任何空闲区相邻,将回收区作为单独的一个空闲盘区挂到链尾。
  • 离散分配、连续分配都适用。为一个文件分配多个盘块时效率更高

image

位示图法

位示图:每个二进制位对应一个盘块。在本例中,“0”代表盘块空闲,“1”代表盘块已分配。位示图一般用连续的“字”来表示,如本例中一个字的字长是16位,字中的每一位对应一个盘块。因此可以用(字号,位号)对应一个盘块号。当然有的题目中也描述为(行号,列号)

(字号,位号)=(i j) 的二进制位对应的盘块号 b= ni + j

b号盘块对应的字号i = b/n,位号j = b%n。

如何分配:若文件需要K个块,

①顺序扫描位示图,找到K个相邻或不相邻的“0”;

②根据字号、位号算出对应的盘块号,将相应盘块分配给文件;

③将相应位设置为“1”。

如何回收:

①根据回收的盘块号计算出对应的字号、位号;

②将相应二进制位设为“0”。

image

成组链接法

空闲表法、空闲链表法不适用于大型文件系统,因为空闲表或空闲链表可能过大。UNIX系统中采用了成组链接法对磁盘空闲块进行管理。
文件卷的目录区中专门用一个磁盘块作为“超级块”,当系统启动时需要将超级块读入内存。并且要保证内存与外存中的“超级块”数据一致。如图所示。

image

  • 超级块中存储的内容

image

  • 如何分配?
    Eg :需要100个空闲块
    ①检查第一个分组的块数是否足够。100=100,是足够的。

    ②分配第一个分组中的100个空闲块。但是由于300号块内存放了再下一组的信息,因此300号块的数据需要复制到超级块中。

    Eg :需要1个空闲块
    ①检查第一个分组的块数是否足够。1<100,因此是足够的。

    ②分配第一个分组中的1个空闲块,并修改相应数据

  • 如何回收?
    Eg :假设每个分组最多为100个空闲块,此时第一个分组已有99个块,还要再回收一块。

    Eg : 假设每个分组最多为100个空闲块,此时第一个分组已有100个块,还要再回收一块。需要将超级块中的数据复制到新回收的块中,并修改超级块的内容,让新回收的块成为第一个分组。

文件的基本操作

创建文件

进行Create系统调用时,需要提供的几个主要参数:

  1. 所需的外存空间大小(如:一个盘块,即1KB)

  2. 文件存放路径(“D:/Demo”)

  3. 文件名(这个地方默认为“新建文本文档.txt”)

    操作系统在处理Create系统调用时,主要做了两件事:

1.在外存中找到文件所需的空间(结合上小节学习的空闲链表法、位示图、成组链接法等管理策略,找到空闲空间)
2.根据文件存放路径的信息找到该目录对应的目录文件(此处就是 D:/Demo目录),在目录中创建该文件对应的目录项。目录项中包含了文件名、文件在外存中的存放位置等信息。

删除文件

进行Delete系统调用时,需要提供的几个主要参数:

1.文件存放路径(“D:/Demo”)

2.文件名(“test.txt”)

操作系统在处理Delete系统调用时,主要做了几件事:

1.根据文件存放路径找到相应的目录文件,从目录中找到文件名对应的目录项。

2.根据该目录项记录的文件在外存的存放位置、文件大小等信息,回收文件占用的磁盘块。(回收磁盘块时,根据空闲表法、空闲链表法、位图法等管理策略的不同,需要做不同的处理)

3.从目录表中删除文件对应的目录项。

打开文件

在很多操作系统中,在对文件进行操作之前,要求用户先使用open系统调用“打开文件”,需要提供的几个主要参数:
1.文件存放路径(“D:/Demo”)

2.文件名( “test.txt”)

3.要对文件的操作类型(如:r只读;rw读写等)

操作系统在处理open系统调用时,主要做了几件事:

1.根据文件存放路径找到相应的目录文件,从目录中找到文件名对应的的目录项,并检查该用户是否有指定的操作权限。

2.将目录项复制到内存中的“打开文件表”中。并将对应表目的编号返回给用户。之后用户使用打开文件表的编号来指明要操作的文件。

image

  • 需要注意的是,有两张打开文件表,一个是进程自带的,另一个是系统的(只有一张)。

image

关闭文件

进程使用完文件后,要“关闭文件”。
操作系统在处理Close系统调用时,主要做了几件事:

1.将进程的打开文件表相应表项删除

2.回收分配给该文件的内存空间等资源

3.系统打开文件表的打开计数器count 减1,若count =0,则删除对应表项。

读文件

进程使用read系统调用完成写操作。

需要指明是哪个文件(在支持“打开文件”操作的系统中,只需要提供文件在打开文件表中的索引号即可),

还需要指明要读入多少数据(如:读入1KB)、

指明读入的数据要放在内存中的什么位置。

操作系统在处理read 系统调用时,会从读指针指向的外存中,将用户指定大小的数据读入用户指定的内存区域中。

写文件

进程使用write系统调用完成写操作,

需要指明是哪个文件(在支持“打开文件”操作的系统中,只需要提供文件在打开文件表中的索引号即可),

还需要指明要写出多少数据(如:写出1KB)、

写回外存的数据放在内存中的什么位置

操作系统在处理write系统调用时,会从用户指定的内存区域中,将指定大小的数据写回写指针指向的外存。

文件共享

基于索引节点的共享方式(硬链接)

知识回顾:索引结点,是一种文件目录瘦身策略。由于检索文件时只需用到文件名,因此可以将除了文件名之外的其他信息放到索引结点中。这样目录项就只需要包含文件名、索引结点指针。

image

索引结点中设置一个链接计数变量 count,用于表示链接到本索引结点上的用户目录项数。
若count =2,说明此时有两个用户目录项链接到该索引结点上,或者说是有两个用户在共享此文件。若某个用户决定“删除”该文件,则只是要把用户目录中与该文件对应的目录项删除,且索引结点的count值减1。
若count>0,说明还有别的用户要使用该文件,暂时不能把文件数据删除,否则会导致指针悬空。当count =0时系统负责删除文件。

基于符号链的共享方式(软链接)

当User3访问“ccc”时,操作系统判断文件“ccc”属于Link类型文件,于是会根据其中记录的路径层层查找目录,最终找到User1的目录表中的“aaa”表项,于是就找到了文件1的索引结点。类似于快捷方式。

image

文件保护

口令保护

为文件设置一个“口令”(如: abc112233),用户请求访问该文件时必须提供“口令”。

口令一般存放在文件对应的FCB或索引结点中。用户访问文件前需要先输入“口令”,操作系统会将用户提供的口令与FCB中存储的口令进行对比,如果正确,则允许该用户访问文件。

优点:保存口令的空间开销不多,验证口令的时间开销也很小。

缺点:正确的“口令”存放在系统内部,不够安全。

加密保护

使用某个“密码”对文件进行加密,在访问文件时需要提供正确的“密码”才能对文件进行正确的解密。
Eg:一个最简单的加密算法――异或加密。
假设用于加密/解密的“密码”为“01001”。

image

优点:保密性强,不需要在系统中存储“密码”。

缺点:编码/译码,或者说加密/解密要花费一定时间。

访问控制

在每个文件的FCB(或索引结点)中增加一个访问控制列表(Access-Control List, ACL),该表中记录了各个用户可以对该文件执行哪些操作。如图所示:

image

精简的访问列表:以“组”为单位,标记各“组”用户可以对文件执行哪些操作。如:分为系统管理员、文件主、文件主的伙伴、其他用户几个分组。当某用户想要访问文件时,系统会检查该用户所属的分组是否有相应的访问权限。

image

总结

image

文件的层次结构

image

用一个例子来辅助记忆文件系统的层次结构:

假设某用户请求删除文件“D:/工作目录/学生信息.xlsx”的最后100条记录。

  1. 用户需要通过操作系统提供的接口发出上述请求一用户接口。
  2. 由于用户提供的是文件的存放路径,因此需要操作系统一层一层地查找目录,找到对应的目录项――文件目录系统
  3. 不同的用户对文件有不同的操作权限,因此为了保证安全,需要检查用户是否有访问权限―一存取控制模块(存取控制验证层)
  4. 验证了用户的访问权限之后,需要把用户提供的“记录号”转变为对应的逻辑地址――逻辑文件系统与文件信息缓冲区
  5. 知道了目标记录对应的逻辑地址后,还需要转换成实际的物理地址――物理文件系统
  6. 要删除这条记录,必定要对磁盘设备发出请求――设备管理程序模块
  7. 删除这些记录后,会有一些盘块空闲,因此要将这些空闲盘块回收――辅助分配模块。

磁盘结构

  • 磁盘的表面由一些磁性物质组成,可以用这些磁性物质来记录二进制数据
  • 磁盘的盘面被划分成一个个磁道。这样的一个“圈”就是一个磁道。
  • 一个磁道又被划分成一个个扇区,每个扇区就是一个“磁盘块”。各个扇区存放的数据量相同(如1KB)。

image

  • 所有盘面中相对位置相同的磁道组成柱面。

image

  • 可用(柱面号,盘面号,扇区号)来定位任意一个“磁盘块”。

  • 可根据该地址读取一个“块”
    ①根据“柱面号”移动磁臂,让磁头指向指定柱面;
    ②激活指定盘面对应的磁头;
    ③磁盘旋转的过程中,指定的扇区会从磁头下面划过,这样就完成了对指定扇区的读/写。

  • 磁盘分类

image

磁盘调度算法

一次磁盘读/写操作需要的时间

image

  • 寻找时间(寻道时间)Ts:在读/写数据前,将磁头移动到指定磁道所花的时间。
    ①启动磁头臂是需要时间的。假设耗时为s;
    ②移动磁头也是需要时间的。假设磁头匀速移动,每跨越一个磁道耗时为m,总共需要跨越n条磁道。则:
    寻道时间Ts = s + m*n

  • 延迟时间T:通过旋转磁盘,使磁头定位到目标扇区所需要的时间。设磁盘转速为r(单位:转/秒,或转/分),则平均所需的延迟时间T=(1/2)*(1/r)= 1/2r。

    1/r就是转一圈需要的时间。找到目标扇区平均需要转半圈,因此再乘以1/2

  • 传输时间Tt:从磁盘读出或向磁盘写入数据所经历的时间,假设磁盘转速为r,此次读/写的字节数为b,每个磁道上的字节数为N。则:
    传输时间Tt = (1/r)*(b/N) = b/(rN)

    每个磁道要可存N字节的数据,因此b字节的数据需要b/N个磁道才能存储。而读/写一个磁道所需的时间刚好又是转一圈所需要的时间1/r。

  • 总的平均存取时间 T=Ts+ 1/2r + b/(rN)

延迟时间和传输时间都与磁盘转速相关,且为线性相关。而转速是硬件的固有属性,因此操作系统也无法优化延迟时间和传输时间。但是操作系统的磁盘调度算法会直接影响寻道时间。

先来先服务算法

  • 根据进程请求访问磁盘的先后顺序进行调度。

image

最短寻找时间优先(SSTF)

SSTF算法会优先处理的磁道是与当前磁头最近的磁道。可以保证每次的寻道时间最短,但是并不能保证总的寻道时间最短。(其实就是贪心算法的思想,只是选择眼前最优,但是总体未必最优)

image

扫描算法(SCAN)

SSTF算法会产生饥饿的原因在于:磁头有可能在一个小区域内来回来去地移动。为了防止这个问题,可以规定,只有磁头移动到最外侧磁道的时候才能往内移动,移动到最内侧磁道的时候才能往外移动。这就是扫描算法(SCAN)的思想。由于磁头移动的方式很像电梯,因此也叫电梯算法。

image

LOOK调度算法

扫描算法(SCAN)中,只有到达最边上的磁道时才能改变磁头移动方向,事实上,处理了184号磁道的访问请求之后就不需要再往右移动磁头了。LOOK调度算法就是为了解决这个问题,如果在磁头移动方向上已经没有别的请求,就可以立即改变磁头移动方向。(边移动边观察,因此叫LOOK)。

image

循环扫描算法(C—SCAN)

SCAN算法对于各个位置磁道的响应频率不平均,而C-SCAN算法就是为了解决这个问题。规定只有磁头朝某个特定方向移动时才处理磁道访问请求,而返回时直接快速移动至起始端而不处理任何请求。

image

C-LOOK调度算法

C-SCAN 算法的主要缺点是只有到达最边上的磁道时才能改变磁头移动方向,并且磁头返回时不一定需要返回到最边缘的磁道上。C-LOOK算法就是为了解决这个问题。如果磁头移动的方向上已经没有磁道访问请求了,就可以立即让磁头返回,并且磁头只需要返回到有磁道访问请求的位置即可。

image

减少延迟时间的方法

image

假设要连续读取橙色区域的2、3、4扇区:
磁头读取一块的内容(也就是一个扇区的内容)后,需要一小段时间处理,而盘片又在不停地旋转
因此,如果2、3号扇区相邻着排列,则读完2号扇区后无法连续不断地读入3号扇区
必须等盘片继续旋转,3号扇区再次划过磁头,才能完成扇区读入。

结论:磁头读入一个扇区数据后需要一小段时间处理,如果逻辑上相邻的扇区在物理上也相邻,则读入几个连续的逻辑扇区,可能需要很长的“延迟时间”。

image

交替编号

若采用交替编号的策略,即让逻辑上相邻的扇区在物理上有一定的间隔,可以使读取连续的逻辑扇区所需要的延迟时间更小。如图所示。

image

磁盘地址结构的设计

思考:为什么磁盘的物理地址是(柱面号,盘面号,扇区号),而不是(盘面号,柱面号,扇区号)?

答:读取地址连续的磁盘块时,采用(柱面号,盘面号,扇区号)的地址结构可以减少磁头移动消耗的时间

注:不是很理解

错位命名

image

image

磁盘的管理

磁盘初始化

磁盘初始化:
Step 1:进行低级格式化(物理格式化),将磁盘的各个磁道划分为扇区。一个扇区通常可分为头、数据区域(如512B大小)、尾三个部分组成。管理扇区所需要的各种数据结构一般存放在头、尾两个部分,包括扇区校验码(如奇偶校验、CRC循环冗余校验码等,校验码用于校验扇区中的数据是否发生错误)

Step 2:将磁盘分区,每个分区由若干柱面(磁道)组成(即分为我们熟悉的C盘、D盘、E盘)

step 3:进行逻辑格式化,创建文件系统。包括创建文件系统的根目录、初始化存储空间管理所用的数据结构(如位示图、空闲分区表)

image

引导块

  • 计算机开机时需要进行一系列初始化的工作,这些初始化工作是通过执行初始化程序(自举程序)完成的。

  • 初始化程序可以放在ROM(只读存储器)中。ROM中的数据在出厂时就写入了,并且以后不能再修改 。

初始化程序程序(自举程序)放在ROM中存在什么问题?万一需要更新自举程序,将会很不方便,因为ROM中的数据无法更改。如何解决呢?

  • ROM中只存放很小的“自举装入程序”。开机时计算机先运行“自举装入程序”,通过执行该程序就可找到引导块,并将完整的“自举程序”读入内存,完成初始化

  • 完整的自举程序放在磁盘的启动块(即引导块/启动分区)上,启动块位于磁盘的固定位置。

  • 拥有启动分区的磁盘称为启动磁盘或系统磁盘(c:盘)

坏块的管理

  • 坏了、无法正常使用的扇区就是“坏块”。这属于硬件故障,操作系统是无法修复的。应该将坏块标记出来,以免错误地使用到它。
  • 对于简单的磁盘,可以在逻辑格式化时(建立文件系统时)对整个磁盘进行坏块检查,标明哪些扇区是坏扇区,比如:在FAT表上标明。(在这种方式中,坏块对操作系统不透明)
  • 对于复杂的磁盘,磁盘控制器(磁盘设备内部的一个硬件部件)会维护一个坏块链表。
    在磁盘出厂前进行低级格式化(物理格式化)时就将坏块链进行初始化。
    会保留一些“备用扇区”,用于替换坏块。这种方案称为扇区备用。且这种处理方式中,坏块对操作系统透明。

I/O管理

I/O设备的概念

  • “I/O” 就是“输入/输出”(Input/Output)
  • I/O设备就是可以将数据输入到计算机,或者可以接收计算机输出数据的外部设备,属于计算机中的硬件部件。

I/O控制器

含义

  • CPU无法直接控制l/O设备的机械部件,因此I/O设备还要有一个电子部件作为CPU和I/O设备机械部件之间的“中介”,用于实现CPU对设备的控制。
  • 这个电子部件就是I/O控制器,又称设备控制器。CPU可控制I/o控制器,又由I/O控制器来控制设备的机械部件。

功能

image

组成

image

I/O控制方式

程序直接控制方式

  • 完成一次读/写操作的流程图(以读操作为例)

image

  • 流程图

image

中断驱动方式

  • 由于程序直接控制方式CPU利用率低,忙等,所以提出了中断驱动方式。

image

image

DMA方式

  • 虽然中断驱动方式解决了程序直接控制方式的问题,但是每一次只能读/写一个字,导致CPU频繁切换,耗费了很多时间。于是人们又发明了DMA方式。

image

  • DMA控制器

image

-

image

通道控制方式

  • 通道控制方式是为了解决DMA方式连续存储的问题

image

image

四种方式总结

image

I/O软件层次结构

知识总览

image

用户层软件

  • 用户层软件实现了与用户交互的接口,用户可直接使用该层提供的、与I/o操作相关的库函数对设备进行操作。
  • 用户层软件将用户请求翻译成格式化的I/o请求,并通过“系统调用”请求操作系统内核的服务。

image

设备独立性软件

设备独立性软件,又称设备无关性软件。与设备的硬件特性无关的功能几乎都在这一层实现。

主要功能:

  1. 向上层提供统一的调用接口(如read/write系统调用)
  2. 设备的保护。(原理类似与文件保护。设备被看做是一种特殊的文件,不同用户对各个文件的访问权限是不一样的,同理,对设备的访问权限也不一样。)
  3. 差错处理(设备独立性软件需要对一些设备的错误进行处理)
  4. 设备的分配与回收
  5. 数据缓冲区管理(可以通过缓冲技术屏蔽设备之间数据交换单位大小和传输速度的差异)
  6. 建立逻辑设备名到物理设备名的映射关系;根据设备类型选择调用相应的驱动程序

用户或用户层软件发出I/o操作相关系统调用的系统调用时,需要指明此次要操作的I/o设备的逻辑设备名(eg:去学校打印店打印时,需要选择打印机1/打印机2/打印机3,其实这些都是逻辑设备名)
设备独立性软件需要通过“逻辑设备表(LUT,Logical UnitTable)”来确定逻辑设备对应的物理设备,并找到该设备对应的设备驱动程序。如图所示:

image

操作系统系统可以采用两种方式管理逻辑设备表(LUT) :
第一种方式,整个系统只设置一张LUT,这就意味着所有用户不能使用相同的逻辑设备名,因此这种方式只适用于单用户操作系统。

第二种方式,为每个用户设置一张LUT,各个用户使用的逻辑设备名可以重复,适用于多用户操作系统。系统会在用户登录时为其建立一个用户管理进程,而LUT就存放在用户管理进程的PCB中。

驱动设备

思考:为何不同的设备需要不同的设备驱动程序?

不同设备的内部硬件特性也不同,这些特性只有厂家才知道,因此厂家须提供与设备相对应的驱动程序,CPU执行驱动程序的指令序列,来完成设置设备寄存器,检查设备状态等工作。

image

中断处理程序

当I/o任务完成时,I/o控制器会发送一个中断信号,系统会根据中断信号类型找到相应的中断处理程序并执行。中断处理程序的处理流程如下:

image

总结

image

I/O核心子系统

知识总览

image

  • 这些功能在哪些层次上实现呢?

image

I/O调度

I/O调度:用某种算法确定一个好的顺序来处理各个I/o请求。
如:磁盘调度(先来先服务算法、最短寻道优先算法、SCAN算法、C-SCAN算法、LOOK算法、C-LOOK算法)。

当多个磁盘I/o请求到来时,用某种调度算法确定满足I/o请求的顺序。

同理,打印机等设备也可以用先来先服务算法、优先级算法、短作业优先等算法来确定IV/o调度顺序。

设备保护

操作系统需要实现文件保护功能,不同的用户对各个文件有不同的访问权限(如:只读、读和写等)。
在UNIx系统中,设备被看做是一种特殊的文件,每个设备也会有对应的FCB。当用户请求访问某个设备时,系统根据FCB中记录的信息来判断该用户是否有相应的访问权限,以此实现“设备保护”的功能。(参考“文件保护”小节)

假脱机技术(SPOOLing技术)

脱机技术

  • 手工操作阶段:主机直接从l/o设备获得数据,由于设备速度慢,主机速度很快。人机速度矛盾明显,主机要浪费很多时间来等待设备。因此在批处理阶段引入了脱机输入/输出技术(用磁带完成).

Tips:为什么称为“脱机”?一脱离主机的控制进行的输入/输出操作。

image

假脱机技术

“假脱机技术”,又称“SPOOLing 技术”,用软件的方式模拟脱机技术。SPOQLing系统的组成如下:

  • “输入井”模拟脱机输入时的磁带,用于收容I/o设备输入的数据
  • “输出井”模拟脱机输出时的磁带,用于收容用户进程输出的数据
  • “输入进程”模拟脱机输入时的外围控制机
  • “输出进程”模拟脱机输出时的外围控制机

image

要实现SPOOLing 技术,必须要有多道程序技术的支持。系统会建立“输入进程”和“输出进程”。

设备的分配和回收

知识总览

image

设备分配时考虑的因素

image

固有属性

设备的固有属性可分为三种:独占设备、共享设备、虚拟设备。

  • 独占设备—— 一个时段只能分配给一个进程(如打印机)
  • 共享设备――可同时分配给多个进程使用(如磁盘),各进程往往是宏观上同时共享使用设备,而微观上交替使用。
  • 虚拟设备――采用SPOOLing 技术将独占设备改造成虚拟的共享设备,可同时分配给多个进程使用(如采用SPOOLing技术实现的共享打印机)

分配算法

先来先服务,优先级高者优先,短任务优先…….

安全性

从进程运行的安全性上考虑,设备分配有两种方式:

安全分配方式:为进程分配一个设备后就将进程阻塞,本次I/O完成后才将进程唤醒。(eg:考虑进程请求打印机打印输出的例子)

  • 一个时段内每个进程只能使用一个设备
  • 优点:破坏了“请求和保持”条件,不会死锁
  • 缺点:对于一个进程来说,CPU和I/o设备只能串行工作

不安全分配方式:进程发出I/o请求后,系统为其分配I/O设备,进程可继续执行,之后还可以发出新的I/o请求。只有某个l/o请求得不到满足时才将进程阻塞。

  • 一个进程可以同时使用多个设备
  • 优点:进程的计算任务和I/o任务可以并行处理,使进程迅速推进
  • 缺点:有可能发生死锁(死锁避免、死锁的检测和解除)

静态分配和动态分配

  • 静态分配:进程运行前为其分配全部所需资源,运行结束后归还资源。破坏了“请求和保持”条件,不会发生死锁
  • 动态分配:进程运行过程中动态申请设备资源

设备分配中的数据结构

“设备、控制器、通道”之间的关系:

image

  • **设备控制表(DCT):**系统为每个设备配置一张DCT,用于记录设备情况

image

  • **控制器控制表(COCT):**每个设备控制器都会对应一张COCT。操作系统根据COCT的信息对控制器进行操作和管理。

image

  • **通道控制表(CHCT):**每个通道都会对应一张CHCT。操作系统根据CHCT的信息对通道进行操作和管理。

image

  • 系统设备表(SDT):记录了系统中全部设备的情况,每个设备对应一个表目。

image

设备分配的步骤

①根据进程请求的物理设备名查找SDT(注:物理设备名是进程请求分配设备时提供的参数)

②根据SDT找到DCT,若设备忙碌则将进程PCB挂到设备等待队列中,不忙碌则将设备分配给进程。

③根据DCT找到COCT,若控制器忙碌则将进程PCB挂到控制器等待队列中,不忙碌则将控制器分配给进程。

④根据COCT找到CHCT,若通道忙碌则将进程PCB挂到通道等待队列中,不忙碌则将通道分配给进程。

注∶只有设备、控制器、通道三者都分配成功时,这次设备分配才算成功,之后便可后动/O设备进行数据传送。

缺点:
①用户编程时必须使用“物理设备名”,底层细节对用户不透明,不方便编程

②若换了一个物理设备,则程序无法运行

③若进程请求的物理设备正在忙碌,则即使系统中还有同类型的设备,进程也必须阻塞等待

改进方法:建立逻辑设备名与物理设备名的映射机制,用户编程时只需提供逻辑设备名

image

设备分配步骤的改进

①根据进程请求的逻辑设备名查找SDT(注:用户编程时提供的逻辑设备名其实就是“设备类型”)

②查找SDT,找到用户进程指定类型的、并且空闲的设备,将其分配给该进程。操作系统在逻辑设备表(LUT)中新增一个表项。

③根据DCT找到cOCT,若控制器忙碌则将进程PCB挂到控制器等待队列中,不忙碌则将控制器分配给进程。

④根据COCT找到CHCT,若通道忙碌则将进程PCB挂到通道等待队列中,不忙碌则将通道分配给进程。

image

  • 逻辑设备表

image

缓冲区管理

知识总览

image

含义和作用

含义

缓冲区是一个存储区域,可以由专门的硬件寄存器组成,也可利用内存作为缓冲区。

使用硬件作为缓冲区的成本较高,容量也较小,一般仅用在对速度要求非常高的场合(如存储器管理中所用的联想寄存器,由于对页表的访问频率极高,因此使用速度很快的联想寄存器来存放页表项的副本)

一般情况下,更多的是利用内存作为缓冲区,“设备独立性软件”的缓冲区管理就是要组织管理好这些缓冲区

作用

image

单缓冲

假设某用户进程请求某种块设备读入若干块的数据。若采用单缓冲的策略,操作系统会在主存中为其分配一个缓冲区(若题目中没有特别说明,一个缓冲区的大小就是一个块)。如图所示
注意:当缓冲区数据非空时,不能往缓冲区冲入数据,只能从缓冲区把数据传出;当缓冲区为空时,可以往缓冲区冲入数据,但必须把缓冲区充满以后,才能从缓冲区把数据传出。

image

  • 处理一块数据的平均时间

image

双缓冲

假设某用户进程请求某种块设备读入若干块的数据。若采用双缓冲的策略,操作系统会在主存中为其分配两个缓冲区(若题目中没有特别说明,一个缓冲区的大小就是一个块)。

  • 假设T>C+M

image

  • 假设T<C+M

image

**结论:采用双缓冲策略,处理一个数据块的平均耗时为Max (T,C+M)**。

循环缓冲区

将多个大小相等的缓冲区链接成一个循环队列。
注:以下图示中,橙色表示已充满数据的缓冲区,绿色表示空缓冲区。

image

缓冲池

缓冲池由系统中共用的缓冲区组成。

这些缓冲区按使用状况可以分为:空缓冲队列、装满输入数据的缓冲队列(输入队列)、装满输出数据的缓冲队列(输出队列)。

另外,根据一个缓冲区在实际运算中扮演的功能不同,又设置了四种工作缓冲区:用于收容输入数据的工作缓冲区(hin)、用于提取输入数据的工作缓冲区(sin)、用于收容输出数据的工作缓冲区(hout) 、用于提取输出数据的工作缓冲区(sout)。

image


操作系统原理
https://moonfordream.github.io/posts/操作系统原理/
作者
Moon
发布于
2024年5月19日
许可协议