第五章:IO管理

IO 管理概述

什么是 IO 设备

在第一章中,我们提到了操作系统应该提供处理机、存储器、文件、设备四方面的管理,前面介绍了处理机,存储器和文件的管理,而设备管理指的就是 IO 设备

IO 设备就是输入输出设备,也就外设,与处理机存储器和磁盘不同,IO 设备都是独立于系统之外的,例如鼠标键盘显示器这种,也包括移动硬盘,这些都是 IO 设备。从名字也可以看出,IO 设备就是 input 和 output,也就是负责输入输出的,想鼠标、键盘这些就是将数据输入到计算机,所以是输入设备;而显示器这种就是将数据输出到屏幕上,就是输出设备。而移动硬盘这种就是又可以输入又可以输出的设备。

UNIX 系统将 IO 设备抽象为一个特殊文件,用户可以使用和文件操作相同的方式操作 IO 设备,即通过 write 操作就可以向 IO 设备里面写数据,read 操作就可以从 IO 设备中读数据。

接下来考虑 IO 设备的几种分类,首先按照使用特性分,也就是从功能上分,IO 设备可以分为人机交互设备、存储设备、网络通信设备。这其实也很好理解,人机交互设备就是键盘鼠标这些,存储设备就是外接硬盘这些,而网络通信设备则是光猫网线这些。

从速度上来分,IO 设备还可以分为低速、中速、高速三种,这个速度划分没有清晰的界限,一般像鼠标键盘这种就是低速设备,打印机这种就是中速设备,磁盘这种就是高速设备。

根据信息交换的单位来分,又可以分为块设备和字符设备,块设备交换信息的基本单位是一个块,比如磁盘就是以磁盘块为单位进行交换的,而字符设备交换信息的单位就是字符,比如鼠标,键盘这种。块设备的传输速率较高,可寻址,即对它可随机地读/写任一块。而字符设备传输速率较慢,不可寻址,在输入/输出时常采用中断驱动方式

IO 控制器

IO 设备主要由机械部件和电子部件组成,机械部件用于对执行具体的 IO 操作,而电子部件则是 IO 接口芯片,这个接口需要完成对机械部件的控制,这个接口就是 IO 控制器。

IO 控制器又称为设备管理器,其负责的主要功能如下

  • 接受和识别 CPU 发出的命令
  • 给 CPU 返回当前设备的状态
  • 数据交换
  • 地址识别

image.png

在 IO 设备中需要存储三种信息,数据、控制、状态。分别用于传输数据、存放控制命令、交换状态信息。而由于需要对这些寄存器进行管理,所以 IO 设备也应该实现地址识别的操作。一个 IO 控制器可能会用于管理多个设备,所以就可能有多个和设备相连的接口。

为了实现对 IO 接口的管理,IO 控制器与其内部的寄存器也应该被编址,由于 IO 控制器内可能有多个寄存器,所以一个 IO 可能会占用多个地址。对 IO 的编址方式有两种,分别是独立编址和统一编址。统一编址也称为内存映像 IO,是将 IO 的地址作为内存地址的一部分,这样我们对 IO 的操作就可以完全看做是对内存的操作。而独立编址则是给 IO 进行专门的编址,当我们寻址的时候可以通过一些信号来判断我们寻找的是 IO 还是内存。同时也需要设计单独的 IO 操作指令来对 IO 进行操作。

image.png

如果读者学过微机原理与接口技术这门课程,那么这里理解起来其实很容易,如果学过微机原理,那就一定学过一个叫 8255 的芯片,叫做并行接口芯片,这就是一个典型的 IO 控制器,是专门用来管理 IO 的。在这里我们的 IO 控制器中会有很多寄存器,其实就是微机原理中的 IO 端口,一个端口就是一个地址,而独立编制和统一编址的概念在微机原理中也会涉及到,并且还应该知道 8086 采用的就是独立编制,通过 IO/M 引脚信号来判断当前的地址类型。同时也应该知道在 8086 中使用 IN 和 OUT 指令来实现对接口输入输出操作。

IO 控制方式

本节介绍 IO 控制方式,实际上也就是 IO 设备输入输出的方式,主要学习四种

程序直接驱动

程序直接驱动其实就是不借助硬件的帮助,直接通过 IO 操作相关指令完成的驱动方式。

程序直接驱动方式而言,我们主要掌握轮循方式。我们知道 IO 设备需要空闲的时候才能为我们服务,当我们发送一个指令请求 IO 服务的时候,我们会告诉 IO 控制器我们需要 IO 服务,也就是发送一个 IO 指令,比如以读指令为例,此时 IO 控制器会将状态位置为 0,表示现在还没有准备好,此时 CPU 发现还没有准备好,就会一直轮循访问这个状态位,CPU 在轮循访问这个状态位的过程中,IO 控制器就会去对 IO 设备进行读操作,并且会查看 IO 设备的状态,如果 IO 设备还没有准备好,那么 IO 控制器和 CPU 都会一直在这等着,直到 IO 设备准备好了之后,此时说明 IO 接口上的寄存器上已经存好了我们的数据了,这个时候 IO 控制器就会将其与 IO 接口相连的寄存器中读入数据,并放到自己的数据寄存器上,然后修改状态位,修改状态位之后,CPU 就知道数据已经在数据寄存器上了,就可以将数据寄存器里的数据通过数据总线读到 CPU 内部的寄存器,然后再写入内存。我们可以用一个流程图来表示。

image.png

以上就是轮循方式的读写流程,这种方式中,CPU 会一直循环判断这个 IO 是否准备好,显然是影响效率的,而且由于数据寄存器的大小限制,每次读写只能读写一个字的数据。

这种方式的数据流向为
读操作(数据输入):I/O 设备->CPU->内存
写操作(数据输出):内存->CPU->I/O 设备
每个字的读/写都需要 CPU 的帮助

综上,主要优缺点可以归纳如下:
优点:实现简单。在读/写指令之后,加上实现循环检查的一系列指令即可(因此才称为“程序直接控制方式”)
缺点:CPU 和 I/O 设备只能串行工作,CPU 需要一直轮询检查,长期处于“忙等”状态,CPU 利用率低。

中断驱动方式

引入中断机制。由于 I/O 设备速度很慢,因此在 CPU 发出读/写命令后,可将等待 I/O 的进程阻塞,先切换到别的进程执行。当 I/O 完成后,控制器会向 CPU 发出一个中断信号,CPU 检测到中断信号后,会保存当前进程的运行环境信息,转去执行中断处理程序处理该中断。处理中断的过程中,CPU 从 I/O 控制器读一个字的数据传送到 CPU 寄存器,再写入主存。接着,CPU 恢复等待 I/O 的进程(或其他进程)的运行环境,然后继续执行。

  1. CPU 会在每个指令周期的末尾检查中断;
  2. 中断处理过程中需要保存、恢复进程的运行环境,这个过程是需要一定时间开销的。可见,如果中断发生的频率太高,也会降低系统性能

以下流程图可以用来表示该种方式
image.png

使用中断的方式,每次 I/O 操作开始之前、完成之后需要 CPU 介入。等待 I/O 完成的过程中 CPU 可以切换到别的进程执行。相较于程序直接驱动而言不需要过多的干预。受 IO 控制器寄存器长度的限制,每次读/写一个字。

数据的流向:
读操作(数据输入):I/O 设备->CPU->内存
写操作(数据输出):内存->CPU->I/O 设备

主要缺点和主要优点
优点:与“程序直接控制方式”相比,在“中断驱动方式”中,I/O 控制器会通过中断信号主动报告 I/O 已完成,CPU 不再需要不停地轮询。CPU 和 I/O 设备可并行工作,CPU 利用率得到明显提升。
缺点:每个字在 I/O 设备与内存之间的传输,都需要经过 CPU。而频繁的中断处理会消耗较多的 CPU 时间。

DMA 方式

与“中断驱动方式”相比,DMA 方式( Direct Memory Access,直接存储器存取。主要用于块设备的 I/O 控制)有这样几个改进:

  1. 数据的传送单位是“块”。不再是一个字、一个字的传送;
  2. 数据的流向是从设备直接放入内存,或者从内存直接到设备。
  3. 仅在传送一个或多个数据块的开始和结束时,才需要 CPU 干预。

DMA 方式的传输步骤如下:

  1. CPU 指明此次要进行的操作(如:读操作),并说明要读入多少数据、数据要存放在内存的什么位置、数据在外部设备上的地址(如:在磁盘上的地址)
  2. 控制器会根据 CPU 提出的要求完成数据的读/写工作,整块数据的传输完成后,才向 CPU 发出中断信号
    image.png

DMA 内部构造如下:

image.png

DMA 中有四个最为重要的寄存器,在上图中已经画出:
DR (Data Register,数据寄存器):暂存从设备到内存,或从内存到设备的数据。
MAR (Memory Address Register,内存地址寄存器):在输入时,MAR 表示数据应放到内存中的什么位置;输出时 MAR 表示要输出的数据放在内存中的什么位置。
DC (Data Counter,数据计数器):表示剩余要读/写的字节数。
CR(Command Register,命令/状态寄存器):用于存放 CPU 发来的 I/O 命令,或设备的状态信息。

DMA 其实是把内存和 IO 设备直接联系在一起的一个桥梁,有了 DMA 之后就允许 IO 设备直接和内存通信,数据就不再需要经过 CPU 中转。所以这种方式中只有开始和结束两个时候 CPU 会进行干涉,其他时候 CPU 都是不管的。我们总结如下:

CPU 干预的频率:仅在传送一个或多个数据块的开始和结束时,才需要 CPU 干预。
数据传送的单位:每次读/写一个或多个块(注意:每次读写的只能是连续的多个块,且这些块读入内存后在内存中也必须是连续的)
数据的流向(不再需要经过 CPU):
读操作(数据输入):I/O 设备->内存
写操作(数据输出):内存->I/O 设备

主要缺点和主要优点
优点:数据传输以“块”为单位,CPU 介入频率进一步降低。数据的传输不再需要先经过 CPU 再写入内存,数据传输效率进一步增加。CPU 和 I/O 设备的并行性得到提升。
缺点:CPU 每发出一条 I/O 指令,只能读/写一个或多个连续的数据块。如果要读/写多个离散存储的数据块,或者要将数据分别写到不同的内存区域时,CPU 要分别发出多条 I/O 指令,进行多次中断处理才能完成。

通道控制方式

通道:一种硬件,可以理解为是 “弱鸡版的 CPU”。通道可以识别并执行一系列通道指令

与 CPU 相比,通道可以执行的指令很单一,并且通道程完成一次读/写操作的流程(见右图) 序是放在主机内存中的,也就是说通道与 CPU 共享内存

通道方式的读写流程如下:
image.png

简而言之,我们需要先在内存中写好一个通道程序,这个通道程序就用于实现 IO 设备和内存的数据交换,我们放到内存后,就可以告诉通道我们需要 IO 请求,并把这个通道程序的首地址给通道,这个通道本身可以自己执行通道程序,所以整个数据交换过程都是由通道程序来处理的。

image.png

通道方式的 CPU 干预的频率极低,通道会根据 CPU 的指示执行相应的通道程序,只有完成一组数据块的读/写后才需要发出中断信号,请求 CPU 干预。数据传送以块为单位。

数据的流向(在通道的控制下进行)
读操作(数据输入):I/O 设备->内存
写操作(数据输出):内存->I/O 设备

主要缺点和主要优点
缺点:实现复杂,需要专门的通道硬件支持
优点:CPU、通道、I/O 设备可并行工作,资源利用率很高。

image.png

IO 软件的层次结构

image.png

以上为 IO 软件的层次结构,当用户发送一个 IO 请求的时候,会一层一层进行处理,最后交给硬件,当硬件处理完成之后,又会一层一层地向上处理,最后交给用户。以上层次结构中,设备驱动程序和中断处理程序都是会直接和硬件打交道的。下介绍各个层次要完成的具体功能

用户层软件

首先,用户层下面是设备独立性软件,设备独立性软件会为我们提供一些系统调用接口,但这些接口可能也过于底层,不适合直接和用户交互,所以用户层软件就会把下一层提供的系统调用接口再次封装成更加友好的接口,也就是向用户提供一些库函数,用户层软件提供的这些友好接口就是直接与用户交互的接口,例如 windowsAPI 就是用户层软件将 windows 提供的系统调用接口再次封装来的。

例如,用户调用库函数 printf 向显示屏输入数据,此时用户层软件就会将 printf 翻译为等价的系统调用,然后调用下一层提供的系统调用来完成请求。

设备独立性软件

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

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

下面主要介绍一下第六点,也就是建立一个逻辑设备名向物理设备名的映射关系。所谓的逻辑设备名就是用户看到的设备名,而物理设备名实际上就是操作系统对这些物理设备的真实编码,用来识别这个设备。设备独立性软件需要通过“逻辑设备表(LUT,Logical UnitTable)”来确定逻辑设备对应的物理设备,并找到该设备对应的设备驱动程序。

image.png

以上就是一个逻辑设备表,用于实现物理设备到逻辑设备的映射,这张表还应该提供驱动程序的入口地址。

操作系统对逻辑设备表的维护有两种方式,操作系统可以在整个操作系统中只维护一个大的逻辑设备表,也可以为每个用户都维护一个逻辑设备表。整个系统只设置一张 LUT,这就意味着所有用不能使用相同的逻辑设备名,因此这种方式只适用于单用户操作系统。为每个用户设置一张 LUT,各个用户使用的逻辑设备名可以重复,适用于多用户操作系统。系统会在用户登录时为其建立一个用户管理进程,而 LUT 就存放在用户管理进程的 PCB 中。

设备驱动层

我们知道操作系统会在系统中维护逻辑地址表,这个逻辑地址表中维护了一个逻辑地址向物理地址的映射关系,并且还维护了驱动程序入口地址,其实就能看到,不同设备的驱动程序入口地址是不同的,哪怕是同一个种类的设备,驱动程序也是不同的。

驱动程序其实就是设备驱动层,这一层干的功能其实就是将上一层的一些命令转化为特定设备能听得懂的信息,比如说设置寄存器等等,因此,这一层会和硬件直接打交道。

不同设备内部构造是不同的,哪怕是同一个类型的设备,其内部实现细节上也是不一样的,所以都需要不同的驱动程序,驱动程序都是由系统生产厂家提供,在操作系统运行过程中,驱动程序会以独立进程的方式存在。

中断处理程序

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

其主要流程如下:

可见,IO 处理层其实也需要直接和硬件打交道。

image.png

输入输出应用程序和设备驱动程序接口

首先,IO 设备的种类其实是多种多样的,主要可以分为字符设备、块设备、网络设备三种。这三种设备有自己不同的特性,比如块设备传输的单位是块,所以是可寻址的,或者说其内部也是有地址这个概念的,比如磁盘。而字符设备比如键盘这种,每次只会读入一个字符,并没有地址这个概念,所以这三类设备本身就不相同。

由于 IO 设备多种多样,不同的设备有不同的特性,所以系统调用就不可能通过一个独立的系统调用来完成所有 IO 请求。针对字符设备、块设备、网络设备我们需要不同的系统调用接口来处理他们。

  • 字符设备的接口:使用 get 和 put 系统调用用于向字符设备读/写一个字符
  • 块设备接口:使用 read/write 系统调用,向块设备的读写指针位置读入或写出多个字符,通过 seek 系统调用修改读写指针的位置。
  • 网络设备接口:使用 socket 系统调用创建一个网络套接字,需要指明网络协议类型。使用 bind 将套接字绑定到某个端口上,使用 connect 调用将套接字连接到远程的地址,使用 read/write 调用从套接字上读/写数据

网络设备的工作流程如下图:
image.png

接下来补充一个概念,阻塞 IO 和非阻塞 IO,在[[第二章:进程管理]] 我们介绍过,使用 IO 设备会导致进程进入阻塞态,但那里只是方便理解,并不严谨,并不是所有的 IO 操作都会导致阻塞,所以根据 IO 操作会不会导致阻塞这个角度来看,可以分为阻塞 IO 和非阻塞 IO,阻塞 IO 的系统调用会使进程变为阻塞态,例如 get 操作,会等待用户输入,而非阻塞 IO 则不会导致进程进入阻塞态,例如 write 操作,这种非阻塞 IO 在 IO 请求发出之后进程就可以继续执行了,不需要等待 IO 返回数据。

最后需要补充介绍一下设备驱动程序接口,我们知道,不同 IO 设备由于实现细节不同,其设备驱动程序也是不同的,设备驱动程序会向操作系统提供一些接口,以供操作系统调用,但如果不同驱动程序提供的接口 API 是不一样的,那操作系统就不知道如何调用了,所以操作系统肯定会要求设备驱动程序向上提供的接口 API 必须是统一的,即统一标准的设备驱动程序,这个设备想要在我这个系统运行就必须按照我这个接口要求开发出一套设备驱动程序。由于不同的系统要求的 API 规范不同,所以在下载驱动的时候会让你选系统。

IO 核心子系统

概述

IO 核心子系统是什么可以参照 [[#IO 软件的层次结构]],IO 层次中,中间三层,也就是设备独立性软件、设备驱动程序、中断处理程序就是 IO 核心子系统。

我们本节主要介绍的是 IO 核心子系统需要提供的部分功能,如下

  1. IO 调度
  2. 设备保护
  3. 假脱机技术(SPOOLing 技术)
  4. 设备的分配和回收
  5. 缓冲区管理 (缓冲与高速缓存)

image.png

上图给出了这五个功能分别要在哪一个层次实现,虽然假脱机技术并不属于 IO 核心子系统要提供的功能,但也放在本节介绍。

在概述部分,我们可以介绍两个比较简单的功能,IO 调度和设备保护,之所以他们简单是因为我们在前面都已经介绍过这两个功能。

IO 调度即通过一种调度算法确定一个顺序来处理各个 IO 请求,例如我们前面提到的磁盘调度算法,当然想打印机这种设备其实也可以用先来先服务算法、优先级算法、短作业优先等算法来确定 I/O 调度顺序。

设备保护这个功能也很简单,因为在操作系统里,IO 设备通常也被看成是一个文件,用户对 IO 的操作可以抽象为对这个文件的操作,所以设备保护的功能也就是文件保护,当操作系统将 IO 设备看做一个文件后,也会为这个文件设立一个 FCB,通过 FCB 中的权限控制表就可以判断该用户是否有权限访问这个 IO 设备,从而实现了权限保护,更详细的可以参照[[第四章:文件管理#文件保护|文件保护]]

假脱机技术(SPOOLing )

首先回顾脱机技术,脱机技术是第一章介绍的知识,这里可以回顾一下

读者可以回顾一下脱机技术的介绍,脱机技术本质上就是可以总结为这么一段话,在外围控制机的控制下,慢速 IO 数据会先送到一个更快速的设备上,由更高速的设备输入给计算机,计算机输出的时候在外围控制机的作用下将数据先输出到一个较为快读的设备上,再由这个设备输出到输出设备。在介绍脱机技术的时候,我们说到的这个速度更快的设备其实就是磁带。

而 SPOOLing 技术就是在多道系统下使用软件模拟脱机技术的一种方式,其中外围控制机就是进程,速度更快的设备其实就是磁盘。

image.png

上图为假脱机技术的示意图,输入设备想要输入的时候,输入进程会通过输入缓冲区将数据输入到磁盘中的输入井,输出的时候输出进程会将输出井的数据通过输出缓冲区输出给输出设备。由于输入进程和输出进程需要并发执行,所以必须是在多道系统下才可以。

接下来做一个案例分析帮助理解,共享打印机原理分析。

image.png

当多个用户进程提出输出打印的请求时,系统会答应它们的请求,但是并不是真正把打印机分配给他们,而是由假脱机管理进程为每个进程做两件事:

  1. 在磁盘输出井中为进程申请一个空闲缓冲区(也就是说,这个缓冲区是在磁盘上的),并将要打印的数据送入其中;
  2. 为用户进程申请一张空白的打印请求表,并将用户的打印请求填入表中(其实就是用来说明用户的打印数据存放位置等信息的),再将该表挂到假脱机文件队列上。
    当打印机空闲时,输出进程会从文件队列的队头取出一张打印请求表,并根据表中的要求将要打印的数据从输出井传送到输出缓冲区,再输出到打印机进行打印。用这种方式可依次处理完全部的打印任务

虽然系统中只有一个台打印机,但每个进程提出打印请求时,系统都会为在输出井中为其分配一个存储区(相当于分配了一个逻辑设备),使每个用户进程都觉得自己在独占一台打印机,从而实现对打印机的共享。
SPOOLing 技术可以把一台物理设备虚拟成逻辑上的多台设备,可将独占式设备改造成共享设备。

其实 SPOOLing 技术在前面提到过一嘴,参照[[第二章:进程管理#破坏互斥条件]]

设备的分配和回收

设备分配的时候需要关注以下三方面内容

  • 设备的固有属性
  • 设备的分配算法
  • 设备分配的安全性

首先,设备的固有属性主要分为三种

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

设备的分配算法其实就是以前介绍过的一些分配算法,先来先服务、按优先级分配、短作业优先,这些有了前面的基础相信听名字就知道什么意思

而从安全性来考虑,不知道读者是否记得什么是安全性,安全性其实就是会不会造成死锁,会造成就是不安全,不会造成死锁那就是安全。

  • 安全分配方式:为进程分配一个设备后就将进程阻塞,本次 I/O 完成后才将进程唤醒。
    优点:不会死锁
    缺点:一个进程的CPU 和 IO 只能串行工作
  • 不安全分配方式:进程发出 I/O 请求后,系统为其分配 I/O 设备,进程可继续执行,之后还可以发出新的 I/O 请求。只有某个 I/O 请求得不到满足时才将进程阻塞。
    优点:进程的计算任务和 I/O 任务可以并行处理,效率更高
    缺点:有可能发生死锁

站在进程的角度,又分为静态分配和动态分配
静态分配:进程运行前为其分配全部所需资源,运行结束后归还资源,不会发生死锁
动态分配:进程运行过程中动态申请设备资源,有可能发生死锁,需要想办法解决,例如银行家算法

接下来考虑分配的具体流程,先看一下通道,控制器,设备三者的关系
image.png
一个通道可控制多个设备控制器,每个设备控制器可控制多个设备。

所以在操作系统中,需要管理通道,控制器,设备三个类型的东西,自然就需要分别为他们设立一张表来管理

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

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

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

注意一下进程队列,我们前面提到过,当一个进程因为某个 IO 请求发生阻塞的时候,会根据请求原因的不同挂载到一个 IO 队列,实际上就是挂载到这个 IO 队列上了

以上三个表分别完成了对设备、控制器和通道的控制,一个控制表只能控制一个设备,系统中为了把设备放在一起统一起来控制,会设置一个系统设备表。

系统设备表(SDT):记录了系统中全部设备的情况,每个设备对应一个表目。
image.png
有了这张表之后,操作系统就可以把系统中所有设备统一管理起来

接下来给出系统分配 IO 设备的步骤

  1. 根据进程请求的物理设备名查找 SDT(注:物理设备名是进程请求分配设备时提供的参数)
  2. 根据 SDT 找到 DCT,若设备忙碌则将进程 PCB 挂到设备等待队列中,不忙碌则将设备分配给进程。
  3. 根据 DCT 找到 COCT,若控制器忙碌则将进程 PCB 挂到控制器等待队列中,不忙碌则将控制器分配给进程。
  4. 根据 COCT 找到 CHCT,若通道忙碌则将进程 PCB 挂到通道等待队列中,不忙碌则将通道分配给进程。

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

以上分配方式有一定缺陷,主要如下:

  1. 用户编程时必须使用“物理设备名”,底层细节对用户不透明,不方便编程
  2. 若换了一个物理设备,则程序无法运行
  3. 若进程请求的物理设备正在忙碌,则即使系统中还有同类型的设备,进程也必须阻塞等待
    改进方法:建立逻辑设备名与物理设备名的映射机制,用户编程时只需提供逻辑设备名

image.png

改进后的分配流程如下:

  1. 根据进程请求的逻辑设备名查找 SDT(注:用户编程时提供的逻辑设备名其实就是“设备类型”)
  2. 查找 SDT,找到用户进程指定类型的、并且空闲的设备,将其分配给该进程。操作系统在逻辑设备表(LUT)中新增一个表项。
  3. 根据 DCT 找到 COCT,若控制器忙碌则将进程 PCB 挂到控制器等待队列中,不忙碌则将控制器分配给进程。
  4. 根据 COCT 找到 CHCT,若通道忙碌则将进程 PCB 挂到通道等待队列中,不忙碌则将通道分配给进程。

逻辑设备表(LUT)建立了逻辑设备名与物理设备名之间的映射关系。
某用户进程第一次使用设备时使用逻辑设备名向操作系统发出请求,操作系统根据用户进程指定的设备类型(逻辑设备名)查找系统设备表,找到一个空闲设备分配给进程,并在 LUT 中增加相应表项。
如果之后用户进程再次通过相同的逻辑设备名请求使用设备,则操作系统通过 LUT 表即可知道用户进程实际要使用的是哪个物理设备了,并且也能知道该设备的驱动程序入口地址。

逻辑设备表的设置问题:
整个系统只有一张 LUT:各用户所用的逻辑设备名不允许重复,适用于单用户操作系统
每个用户一张 LUT:不同用户的逻辑设备名可重复,适用于多用户操作系统

缓冲区管理

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

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

缓冲区的功能:

  • 缓和 CPU 与 IO 设备之间速度不匹配的矛盾
  • 减少对 CPU 的中断频率,放宽对 CPU 中断响应时间的限制
  • 解决数据粒度不匹配的问题
  • 提高 CPU 与 IO 设备之间的并行性

简而言之,CPU 是一个高速设备,IO 是一个低速设备,CPU 无法直接和 IO 进行通信,当 CPU 想要从 IO 读入数据的时候,只有让 IO 把数据先放到缓冲区,然后再由 CPU 从缓冲区中读。而像一个进程每次生成一个页框的数据,但 IO 设备每次只能接受一个字符的设备,就可以放到缓冲区让 IO 慢慢接受,而不需要一个字符一个字符传送。

当缓冲区数据非空时,不能往缓冲区冲入数据,只能从缓冲区把数据传出;当缓冲区为空时,可以往缓冲区冲入数据,但必须把缓冲区充满以后,才能从缓冲区把数据传出。

单缓冲

假设某用户进程请求某种块设备读入若干块的数据。若采用单缓冲的策略,操作系统会在主存中为其分配一个缓冲区(若题目中没有特别说明,一个缓冲区的大小就是一个块)。
image.png
设备会向缓冲区中输入数据,当缓冲区满之后,用户进程会将缓冲区内的数据读入用户进程的内存空间,在用户进程的内存空间中,会分出一片工作区来接受输入/输出数据(一般也默认工作区大小与缓冲区相同)。

常考题型:计算每处理一块数据平均需要多久?技巧:假定一个初始状态,分析下次到达相同状态需要多少时间,这就是处理一块数据平均所需时间。在“单缓冲”题型中,可以假设初始状态为工作区满,缓冲区空。

image.png

双缓冲

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

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

采用双缓冲和单缓冲在通信的时候也会存在一些区别,由于缓冲区的特性,单缓冲只能实现单工通信,双缓冲才可以实现双工通信

循环缓冲区

将多个大小相等的缓冲区链接成一个循环队列

image.png

缓冲池

缓冲池由系统中共用的缓冲区组成。这些缓冲区按使用状况可以分为:空缓冲队列、装满输入数据的缓冲队列(输入队列)、装满输出数据的缓冲队列(输出队列)。
另外,根据一个缓冲区在实际运算中扮演的功能不同,又设置了四种工作缓冲区:用于收容输入数据的工作缓冲区(hin)、用于提取输入数据的工作缓冲区(sin)、用于收容输出数据的工作缓冲区(hout)、用于提取输出数据的工作缓冲区(sout)

image.png

当进程请求输入数据,也就是输入进程需要从 IO 设备读入数据,就会从空缓冲队列中取出一块放到收容输入的缓冲区上,当缓冲区满了之后就会挂到输入队列的队尾。

当进程请求读取一个输入进程的数据,就会将输入队列的队头放到提取输入的缓冲区供进程读取,当进程读取之后,然后挂到空缓冲队列

当进程处理完数据,需要输出时,就会从空缓冲队列中取出一块放到收容输出缓冲区,当缓冲区满了之后就会挂到输出队列

当进程请求输出数据的时候,就会从输出队列中取出第一个数据,挂到提取输出的缓冲区中,然后由 IO 设备慢慢读,当 IO 设备读完数据后,就会挂到空缓冲队列上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值