• I/O 设备
    • ">I/O 设备的概念
    • ">I/O 设备的类型
    • ">设备与控制器之间的接口
  • ">设备控制器
    • ">设备控制器的基本功能
    • ">设备控制器的组成
    • ">内存映像
  • ">I/O 通道
    • ">引入 I/O 通道的动机
    • ">通道类型
    • ">瓶颈问题
  • ">对 I/O 设备的控制方式
    • ">轮询的可编程 I/O 方式
    • ">中断的可编程l/O方式
    • ">轮询和中断的比较
    • ">直接存储器访问方式
    • ">I/O 通道控制方式
  • ">设备分配
    • ">设备分配中的数据结构
    • ">设备分配的因素
      • ">设备的固有属性
      • ">设备分配算法
      • ">设备分配中的安全性
    • ">设备分配的步骤
  • ">缓冲区管理
    • ">缓冲区
    • ">单缓冲区
    • ">双缓冲区
    • ">环形缓冲区
    • ">缓冲池

    I/O 设备

    I/O 设备的概念

    I/O 就是输入/输出(Input/Output),I/O 设备就是可以将数据输入到计算机,或者可以接收计算机输出数据的外部设备,属于计算机中的硬件部件。I/O 设备一般是由执行 I/O 操作的机械部分和执行控制 I/O 的电子部件组成,I/O 设备的机械部件主要用来执行具体 I/O 操作,例如鼠标/键盘的按钮、显示器的 LED 屏和移动硬盘的磁臂、磁盘盘面。
    CPU 无法直接控制 I/O 设备的机械部件,因此 I/O 设备还要有一个电子部件作为 CPU 和 I/O 设备机械部件之间的“中介”,用于实现 CPU 对设备的控制。这个电子部件就是 I/O 控制器,又称设备控制器,通常是一块插入主板扩充槽的印刷电路板。CPU 可控制 I/O 控制器,又由 I/O 控制器来控制设备的机械部件。

    I/O 设备的类型

    I/O 设备的类型繁多,除了能将它们分为块设备和字符设备、独占设备和共享设备外,还可按使用特性分类和按传输速率分类。按使用特性分类的第一类是存储设备,也称外存、辅存,用以存储信息,存取速度较内存慢但容量大且价格便宜。第二类就是 I/O 设备,它又可分为输入设备、输出设备和交互式设备。

    设备类型 说明
    输入设备 用来接收外部信息,如键盘、鼠标、扫描仪、视频摄像等
    输出设备 用于将计算机处理后的信息送向处理机外部的设备,如打印机、绘图仪等
    交互式设备 则是指集成的上述两类设备,用于同步显示用户命令以及命令执行的结果

    按传输速度的高低,可将 I/O 设备分为三类。

    设备类型 说明
    低速设备 传输速率仅为每秒钟几个字节至数百个字节,例如键盘、鼠标器
    中速设备 传输速率在每秒钟数千个字节至数十万个字节,例如行式打印机、激光打印机等
    高速设备 传输速率在数十万字节至千兆字节,例如磁带机、磁盘机、光盘机等

    设备与控制器之间的接口

    通常设备并不是直接与 CPU 进行通信,而是与设备控制器通信,因此在 I/O 设备中应含有与设备控制器间的接口.
    image.png
    在该接口中有三种类型的信号,各对应一条信号线。

    信号线 说明
    数据信号线 在设备和设备控制器之间传送数据信号
    控制信号线 由设备控制器向 I/O 设备发送控制信号时的通路,信号规定了设备将要执行的操作,如读操作、写操作或执行磁头移动等操作
    状态信号线 该信号线用于传送指示设备当前状态的信号,状态有正在读(或写)、设备已读(写)完成

    设备控制器

    设备控制器的主要功能是控制一个或多个 I/O 设备,以实现 I/O 设备和计算机之间的数据交换。它接收从 CPU 发来的命令,去控制 I/O 设备工作。设备控制器是一个可编址的设备,当它仅控制一个设备时只有一个唯一的设备地址,若控制器可连接多个设备则含有多个设备地址,每一个设备地址对应一个设备。可把设备控制器分成两类:一类是用于控制字符设备的控制器,另一类是用于控制块设备的控制器。

    设备控制器的基本功能

    功能 说明
    接收和识别命令 接收并识别处理机发来的多种命令,利用控制寄存器存放接收的命令和参数,并对所接收的命令进行译码
    数据交换 实现 CPU 与控制器之间、控制器与设备之间的数据交换,前者通过数据总线,后者是通过数据寄存器
    标识和报告设备的状态 控制器应记下设备的状态供 CPU 了解,在控制器中应设置状态寄存器,用其中的一位反映设备的状态
    地址识别 系统中的每一个设备都有一个地址,设备控制器必须能够识别其所控制的每个设备的地址,应配置地址译码器
    数据缓冲区 由于 I/O 设备的速率较低,而 CPU 和内存的速率却很高,故在控制器中必须设置缓冲区
    差错控制 对于由 I/O 设备传送来的数据,若发现传送中出现了错误,通常是将差错检测码置位并向 CPU 报告

    设备控制器的组成

    设备控制器位于 CPU 与设备之间,既要与 CPU 通信又要与设备通信,还应具有按照 CPU 所发来的命令去控制设备工作的功能。
    image.png

    组件 说明
    设备控制器与处理机的接口 用于实现 CPU 与设备控制器之间的通信,在该接口中共有三类信号线:数据线、地址线和控制线
    设备控制器与设备的接口 设备控制器上可以连接多个设备,便有多个设备接口。在每个接口中都存在数据、控制和状态三种类型的信号
    I/O 逻辑 用于实现对设备的控制,它通过一组控制线与处理机交互,处理机利用该逻辑向控制器发送 I/O 命令。

    数据线通常与两类寄存器相连接,第一类是数据寄存器,用于存放从设备送来的数据(输入)或从 CPU 送来的数据(输出)。第二类是控制/状态寄存器,用于存放从CPU送来的控制信息或设备的状态信息。

    内存映像

    驱动程序将抽象 I/O 命令转换出的一系列具体的命令、参数等数据装入设备控制器的相应寄存器,由控制器来执行这些命令实施对 I/O 设备的控制。这一工作在早期的计算机中利用特定的 I/O 指令实现,为每个控制寄存器分配一个 I/O 端口并设置了一些特定的 I/O 指令。该方法的主要缺点是,访问内存和访问设备需要两种不同的指令。
    现在采用的是内存映像 I/O,这种方式中在编址上不再区分内存单元地址和设备控制器中的寄存器地址,而是设置一个 k 值。当 k 值处于 0 ~ n-1 范围时被认为是内存地址,若 k ≥ n 时被认为是某个控制器的寄存器地址。内存映像 I/O 方式统一了对内存和对控制器的访问方法,简化 I/O 的编程。
    image.png

    I/O 通道

    引入 I/O 通道的动机

    虽然在 CPU 与 I/O 设备之间增加了设备控制器后能减少 CPU 对 I/O 的干预,但当主机所配置的外设很多时 CPU 的负担仍然很重。为此在 CPU 和设备控制器之间又增设了I/O 通道(I/O Channel)。主要目的是使一些原来由 CPU 处理的 I/O 任务转由通道来承担,从而把 CPU 从繁杂的 I/O 任务中解脱出来。
    在设置了通道后,CPU 只需向通道发送一条 I/O 指令,通道从内存中取出本次要执行的通道程序然后执行。仅当通道完成了规定的 I/O 任务后,才向 CPU 发中断信号。实际上 I/O 通道是一种特殊的处理机,它具有执行 I/O 指令的能力,通过执行通道 I/O 程序来控制 I/ O操作。但 I/O 通道的指令类型单一,且没有自己的内存。

    通道类型

    外围设备的类型较多,传输速率相差甚大,因而使通道具有多种类型。根据信息交换方式的不同,可把通道分成以下三种类型。

    通道 说明
    字节多路通道(Byte Multiplexor Channel) 按字节交叉方式工作的通道,通常都含有许多非分配型子通道,按时间片轮转方式共享主通道,当第一个子通道控制其 I/O 设备完成一个字节的交换后,便立即腾出主通道让给第二个子通道使用
    数组选择通道(Block Selector Channel) 按数组方式进行数据传送的数组选择通道,可以连接多台高速设备,但它只含有一个分配型子通道,在一段时间内只能执行一道通道程序
    数组多路通道(Block Multiplexor Channel) 将数组选择通道传输速率高和字节多路通道能使各子通道(设备)分时并行操作的优点相结合,它含有多个非分配型子通道,既具有很高的数据传输速率,又能获得令人满意的通道利用率

    瓶颈问题

    由于通道价格昂贵,致使机器中所设置的通道数量势必较少,这往往又使它成了 I/O 的瓶颈,进而造成整个系统吞吐量的下降。例如在图中如果要使用设备 1 需要占用通道 1 和控制器 1,但此时如果还要使用设备 2,会因为通道和控制器数量不足而无法使用。
    image.png
    解决“瓶颈”的方法是增加设备到主机间的通路而不增加通道,也就是把一个设备连接到多个控制器上,而一个控制器又连接到多个通道上。
    image.png

    对 I/O 设备的控制方式

    在 I/O 控制方式的整个发展过程中,始终贯穿着这样一条宗旨:尽量减少主机对 I/O 控制的干预,把主机从繁杂的 I/O 控制事务中解脱。

    轮询的可编程 I/O 方式

    对设备的控制,早期是轮询的可编程 I/O 方式。在处理机向控制器发出一条 I/O 指令,启动输入设备输入数据时,要同时把状态寄存器中的忙/闲标志 busy 置为 1,然后便不断地循环测试 busy(称为轮询)。当 busy = 1 时表示输入机尚未输完一个字(符),处理机应继续对该标志进行测试。直至 busy = 0,表明输入机已将输入数据送入控制器的数据寄存器中。于是处理机将数据寄存器中的数据取出,送入内存指定单元中,这样便完成了一个字(符)的 I/O。
    image.png
    使用轮询的可编程 I/O 方式的优点是实现简单,在读/写指令之后,加上实现循环检查的一系列指令即可。缺点是 CPU 和 I/O 设备只能串行工作,CPU 需要一直轮询检查,长期处于“忙等”状态,CPU 利用率低。

    中断的可编程l/O方式

    当前对 I/O 设备的控制,广泛采用中断的可编程 I/O 方式。当某进程要启动某个 I/O 设备工作时,便由 CPU 向相应的设备控制器发出一条 I/O 命令,然后立即返回继续执行原来的任务。设备控制器于是按照该命令的要求去控制指定 I/O 设备,此时 CPU 与 I/O 设备并行操作。
    image.png
    仅当输完一个数据时,才需 CPU 花费极短的时间去做些中断处理。这样可使 CPU 和 I/O 设备都处于忙碌状态,从而提高了整个系统的资源利用率及吞吐量。例如,从终端输入一个字符的时间约为 100ms,而将字符送入终端缓冲区的时间小于 0.1ms。若采用程序 I/O 方式,CPU 约有 99.9ms 的时间处于忙一等待中。但采用中断驱动方式后,CPU 可利用这 99.9ms 的时间去做其它的事情。该方法的缺点是每个字在 I/O 设备与内存之间的传输,都需要经过 CPU,频繁的中断处理会消耗较多的 CPU 时间。

    轮询和中断的比较

    中断令 CPU 不再需要不断轮询设备,而是向设备发出一个请求,然后就可以让对应进程睡眠,切换执行其他任务。当设备完成了自身操作,会抛出一个硬件中断,引发 CPU 跳转执行操作系统预先定义好的中断处理程序(interrupt handler)。中断允许计算与 I/O 重叠(overlap),这是提高 CPU 利用率的关键,操作系统可以令 CPU 在 I/O 操作时去做其他事情。
    但使用中断并非总是最佳方案,假如有一个非常高性能的设备,它处理请求很快,通常在 CPU 第一次轮询时就可以返回结果。此时如果使用中断,反而会使系统变慢:切换到其他进程处理中断,再切换回之前的进程代价不小。另一个最好不要使用中断的场景是网络,网络端收到大量数据包,如果每一个包都发生一次中断,那么有可能导致操作系统发生活锁。
    image.png
    因此如果设备非常快,那么最好的办法反而是轮询,如果设备比较慢,那么采用允许发生重叠的中断更好。如果设备的速度未知,或者时快时慢,可以考虑使用混合(hybrid)策略,先尝试轮询一小段时间,如果设备没有完成操作,此时再使用中断。另一个基于中断的优化就是合并(coalescing),设备在抛出中断之前往往会等待一小段时间,在此期间,其他请求可能很快完成,因此多次中断可以合并为一次中断抛出,从而降低处理中断的代价。

    直接存储器访问方式

    虽然中断驱动 I/O 比程序 I/O 方式更有效,但它仍是以字(节)为单位进行 I/O 的。采用中断驱动 I/O 方式时的 CPU,是以字(节)为单位进行干预的。如果将这种方式用于块设备的 I/O,显然是极其低效的。与中断驱动方式相比,DMA (Direct Memory Access)直接存储器存取方式有这样几个改进:

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

    当给 I/O 模块发送命令时,CPU 指明此次要进行的操作,并说明要读入多少数据、数据要存放在内存的什么位置数据在外部设备上的地址。控制器会根据 CPU 提出的要求完成数据的读/写工作,整块数据的传输完成后,才向 CPU 发出中断信号。
    image.png
    DMA 控制器由三部分组成:主机与 DMA 控制器的接口、DMA 控制器与块设备的接口、I/O 控制逻辑。
    image.png

    组件 说明
    DR(Data Register,数据寄存器) 暂存从设备到内存,或从内存到设备的数据
    MAR(Memory Address Register,内存地址寄存器) 在输入时 MAR 表示数据应放到内存中的什么位置,输出时 MAR 表示要输出的数据放在内存中的什么位置
    DC(Data Counter,数据计数器) 表示剩余要读/写的字节数。
    CR(Command Register,命令/状态寄存器) 用于存放 CPU 发来的 I/O 命令,或设备的状态信息

    DMA 的优点是数据传输以“块”为单位,CPU 介入频率进一步降低。数据的传输不再需要先经过C PU 再写入内存,数据传输效率进一步增加,CPU 和 I/O 设备的并行性得到提升。缺点是 CPU 每发出一条 I/O 指令,只能读/写一个或多个连续的数据块。

    I/O 通道控制方式

    I/O 通道方式是 DMA 方式的发展,它把对一个数据块的读(或写)为单位的干预,减少为对一组数据块的读(或写)及有关的控制和管理为单位的干预。同时,又可实现 CPU、通道和 I/O 设备三者的并行操作,从而更有效地提高整个系统的资源利用率。不过实现复杂,需要专门的通道硬件支持。
    例如当 CPU 要完成一组相关的读(或写)操作及有关控制时,只需向 I/O 通道发送一条 I/O 指令,以给出其所要执行的通道程序的首址和要访问的 I/O 设备。通道接到该指令后,通过执行通道程序便可完成 CPU 指定的 I/O 任务。

    设备分配

    “设备、控制器、通道”之间的关系是一个通道可控制多个设备控制器,每个设备控制器可控制多个设备。
    image.png

    设备分配中的数据结构

    系统为实现对独占设备的分配,必须在系统中配置相应的数据结构,记录了对设备或控制器进行控制所需的信息。系统为每一个设备都配置了一张设备控制表 DCT,用于记录设备的情况。
    image.png

    字段 说明
    设备队列队首指针 凡因请求本设备而未得到满足的进程,应将其 PCB 按照一定的策略排成一个设备请求队列
    忙/闲标志 用于表示当前设备的状态是忙或闲
    与设备连接的控制器表指针 指向该设备所连接的控制器的控制表
    重复执行次数 设备在工作中发生错误时应重复执行的次数

    系统为每一个控制器都设置了用于记录控制器情况的控制器控制表 COCT。
    image.png
    每个通道都有一张通道控制表 CHCT。
    image.png
    系统设备表 SDT 是系统范围的数据结构,记录了系统中全部设备的情况,每个设备占一个表目,其中包括有设备类型、设备标识符、设备控制表及设备驱动程序的入口等项。
    image.png

    设备分配的因素

    系统在分配设备时,应考虑如下几个因素:

    设备的固有属性

    设备的固有属性可分成三种,对它们应采取不同的分配策略:

    固有属性 说明
    独占设备的分配策略 将一个设备分配给某进程后,便由该进程独占
    共享设备的分配策略 对于共享设备,可同时分配给多个进程使用
    虚拟设备的分配策略 虚拟设备属于可共享的设备

    设备分配算法

    对设备分配的算法,通常只采用以下两种分配算法:

    分配算法 说明
    先来先服务 根据诸进程对某设备提出请求的先后次序,将这些进程排成一个设备请求队列
    优先级高者优先 将优先级高的进程排在设备队列前面,而对于优先级相同的 I/O 请求,则按先来先服务原则排队

    设备分配中的安全性

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

    分配方式 说明
    安全分配方式 为进程分配一个设备后就将进程阻塞,本次 I/O 完成后才将进程唤醒
    不安全分配方式 进程发出 I/O 请求后,系统为其分配 I/O 设备,之后还可以发出新的 I/O 请求

    设备分配的步骤

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

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

    为获得设备的独立性,进程应使用逻辑设备名请求 I/O。这样系统首先从 SDT 中找出第一个该类设备的 DCT,若该设备忙又查找第二个该类设备的 DCT,仅当所有该类设备都忙时,才把进程挂在该类设备的等待队列上。

    缓冲区管理

    缓冲区

    缓冲区是一个存储区域,可以由专门的硬件寄存器组成,也可利用内存作为缓冲区。使用硬件作为缓冲区的成本较高,容量也较小,一般仅用在对速度要求非常高的场合(如存储器管理中所用的联想寄存器)一般情况下利用内存作为缓冲区,“设备独立性软件”的缓冲区管理就是要组织管理好这些缓冲区。
    引入缓冲区的原因有:

    1. 缓和 CPU 与 I/O 设备间速度不匹配的矛盾:CPU 的运算速率远远高于 I/O 设备的速率,如果没有缓冲区,在运行时会因为 I/O 设备跟不上 CPU 的速度导致 CPU 停下来等待;
    2. 减少对 CPU 的中断频率,放宽对 CPU 中断响应时间的限制。随着传输速率的提高,需要配置位数更多的寄存器进行缓冲;
    3. 解决数据粒度不匹配的问题:生产者所生产的数据粒度比消费者小时,生产者进程可以一连生产多个数据单元的数据。生产者比消费者粒度大时,生产者每次生产的数据消费者可以分几次从缓冲区中取出消费;
    4. 提高 CPU 和 I/O 设备之间的并行性:生产者在生产了一批数据并将它放入缓冲区后,便可立即去进行下一次的生产。

      单缓冲区

      假设某用户进程请求某种块设备读入若干块的数据。若采用单缓冲(Single Buffer)的策略,操作系统会在主存中为其分配一个缓冲区。当缓冲区数据非空时,不能往缓冲区冲入数据,只能从缓冲区把数据传出。当缓冲区为空时可以往缓冲区冲入数据,但必须把缓冲区充满以后,才能从缓冲区把数据传出。
      image.png
      假定从磁盘把一块数据输入到缓冲区的时间为 T,OS 将该缓冲区中的数据传送到用户区的时间为 M,CPU 数据处理(计算)的时间为C。由于 T 和 C 是可以并行的,当 T > C 时系统对每一块数据的处理时间为 M + T,反之则为M + C,故可把系统对每一块数据的处理时间表示为 Max(C,T) + M
      image.png

      双缓冲区

      由于缓冲区是共享资源,生产者与消费者在使用缓冲区时必须互斥。如果消费者尚未取走缓冲区中的数据,即使生产者又生产出新的数据,也无法将它送入缓冲区,生产者等待。
      如果设置了两个缓冲区能解决这一问题,同时可以加快输入和输出速度,提高设备利用率。双缓冲区机制(Double Buffer)也称缓冲对换(Buffer Swapping),在设备输入时先将数据送入第一缓冲区,装满后便转向第二缓冲区。此时操作系统可以从第一缓冲区中移出数据,并送入用户进程,接着由 CPU 对数据进行计算。
      image.png
      在双缓冲时系统处理一块数据的时间可以粗略地认为是 Max(C,T),如果 C < T 可使块设备连续输入,如果 C > T 则可使 CPU 不必等待设备输入。对于字符设备,若采用行输入方式,在 CPU 执行第一行中的命令时,用户可继续向第二缓冲区输入下一行数据。
      image.png
      如果在实现两台机器之间的通信时仅为它们配置了单缓冲,那么它们之间在任一时刻只能实现半双工的数据传输。为了实现全双工数据传输,必须在两台机器中都设置两个缓冲区,一个用作发送缓冲区,另一个用作接收缓冲区。
      image.png

      环形缓冲区

      若两个缓冲区的速度相差甚远,双缓冲的效果则不够理想,不过可以通过缓冲区数量的增加来改善。多缓冲机制可将多个缓冲区组织成环形缓冲区形式,在环形缓冲中包括多个缓冲区,其每个缓冲区的大小相同。
      作为输入的多缓冲区可分为三种类型:用于装输入数据的空缓冲区 R、已装满数据的缓冲区 G 以及计算进程正在使用的现行工作缓冲区 C。作为输入的缓冲区可设置三个指针:用于指示计算进程下一个可用缓冲区 G 的指针 Nextg、指示输入进程下次可用的空缓冲区 R 的指针 Nexti,以及用于指示计算进程正在使用的缓冲区 C 的指针 Current。
      image.png
      使用输入循环缓冲,可使输入进程和计算进程并行执行,相应地指针 Nexti 和 Nextg 将不断地沿着顺时针方向移动。这样就可能出现下 Nexti 指针追赶上 Nextg 指针,这意味着输入进程输入数据的速度大于计算进程处理数据的速度。也有可能出现 Nextg 指针追赶上 Nexti 指针,这意味着输入数据的速度低于计算进程处理数据的速度。

      缓冲池

      当系统较大时会存在大量的循环缓冲,这不仅要消耗大量的内存空间,而且其利用率不高。目前广泛流行既可用于输入又可用于输出的公用缓冲池,在池中设置了多个可供若干个进程共享的缓冲区。缓冲池与缓冲区的区别在于,缓冲区仅仅是一组内存块的链表,而缓冲池则是包含了一个管理的数据结构及一组操作函数的管理机制。
      缓冲池管理着多个缓冲区,每个缓冲区由用于标识和管理的缓冲首部以及用于存放数据的缓冲体两部分组成。缓冲首部一般包括缓冲区号、设备号、设备上的数据块号、同步信号量以及队列链接指针等。为了管理上的方便,一般将缓冲池中具有相同类型的缓冲区链接成一个队列,于是可形成以下三个队列。除了三个队列外,还应具有四种工作缓冲区:用于收容输入数据的工作缓冲区、用于提取输入数据的工作缓冲区、用于收容输出数据的工作缓冲区,以及用于提取输出数据的工作缓冲区。
    队列 说明
    空白缓冲队列 emq 由空缓冲区所链成的队列
    输入队列 inq 由装满输入数据的缓冲区所链成的队列
    输出队列 outq 由装满输出数据的缓冲区所链成的队列

    缓冲区可以有如下 4 个工作方式:

    1. 输入进程请求输入数据;
    2. 计算进程想要取得一块输入数据;
    3. 计算进程想要将准备好的数据冲入缓冲区;
    4. 输出进程请求输出数据。

    image.png