OS- -I/O之I/O设备

OS- -I/O之I/O设备

一、I/O

  • 我们之前提到了操作系统的三个抽象,它们分别是进程、地址空间和文件,除此之外,操作系统 还要控制所有的I/O设备。
  • 操作系统必须向设备发送命令,捕捉中断并处理错误。它还应该在设备 和操作系统的其余部分之间提供一个简单易用的接口
  • 不同的人对I/O。硬件的理解也不同。
  • 对于电子工程师而言,I/O硬件就是芯片、导线、电源和其他组成 硬件的物理设备。
  • 而我们程序员眼中的I/O其实就是硬件提供给软件的接口,比如硬件接受到的命
    令、执行的操作以及反馈的错误。

1.I/O设备

  • 什么是I/O设备? I/O设备又叫做输入/输出设备,它是人类用来和计算机进行通信的外部硬件
  • 输入/输 出设备能够向计算机发送数据(输出)并从计算机接收数据(输入)
  • I/O设备(I/O devices)可以分成两种:块设备(block devices)和 字符设备(character devices)。

块设备

  • 块设备是一个能存储固定大小块信息的设备,它支持以固定大小的块,扇区或群集读取和(可选)写 入数据。

  • 每个块都有自己的物理地址。通常块的大小在512 - 65536之间。所有传输的信息都会以连 续的块为单位

  • 块设备的基本特征是每个块都较为对立,能够独立的进行读写

  • 常见的块设备有硬 盘、蓝光光盘、USB盘

  • 与字符设备相比,块设备通常需要较少的引脚

在这里插入图片描述

块设备的缺点

  • 基于给定固态存储器的块设备比基于相同类型的存储器的字节寻址要慢一些,因为必须在块的开头开始 读取或写入
  • 所以,要读取该块的任何部分,必须寻找到该块的开始,读取整个块,如果不使用该块, 则将其丢弃。要写入块的一部分,必须寻找到块的开始,将整个块读入内存,修改数据,再次寻找到块 的开头处,然后将整个块写回设备

字符设备

  • 另一类I/O设备是字符设备。字符设备以字符为单位发送或接收一个字符流,而不考虑任何块结 构
  • 字符设备是不可寻址的,也没有任何寻道操作
  • 常见的字符设备有打印机、网络设备、鼠标、以 及大多数与磁盘不同的设备。
    在这里插入图片描述
  • 下面显示了一些常见设备的数据速率:
    在这里插入图片描述

设备控制器

首先需要先了解一下设备控制器的概念。

  • 设备控制器是处理CPU传入和传出信号的系统。设备通过插头和插座连接到计算机,并且插座连接到设备控制器。设备控制器从连接的设备处接收数据,并将其存储在控制器内部的一些特殊目的寄存器 (special purpose registers)也就是本地缓冲区中。
  • 特殊用途寄存器,顾名思义是仅为一项任务而设计的寄存器
  • 例如,cs, ds, gs和其他段寄存器属于特殊目的寄存器,因为它们的存在是为了保存段号。
  • eax, ecx等是一般用途的寄存器,因为你可以无限制地使用它们
  • 例如,你不能移动ds,但是可以移动eax, ebxo
  • 通用目的寄存器比如有:eax、ecx、edx、ebx、esi、edi、ebp、esp
  • 特殊目的寄存器比如有:cs、ds、ss、es、fs、gs、eips flag
  • 每个设备控制器都会有一个应用程序与之对应,设备控制器通过应用程序的接口通过中断与操作系统进行通信设备控制器是硬件,而设备驱动程序是软件
  • I/O设备通常由 机械组件(mechanical component)和 电子组件(electronic component)构成。电 子组件被称为设备控制器(device controller)或者适配器(adapter)

在个人计算机上,它通 常采用可插入(PCIe)扩展插槽的主板上的芯片或印刷电路卡的形式。
在这里插入图片描述

  • 机械设备就是它自己,它的组成如下:
    在这里插入图片描述
  • 控制器卡上通常会有一个连接器,通向设备本身的电缆可以插入到这个连接器中,很多控制器可以操作 2个、4个设置8个相同的设备。
  • 控制器与设备之间的接口通常是一个低层次的接口。例如,磁盘可能被格式化为2,000,000个扇区,每 个磁道512字节。
  • 然而,实际从驱动出来的却是一个串行的比特流,从一个前导符(preamble)开 始,然后是一个扇区中的4096位,最后是一个校验和 或ECC (错误码,Error-Correcting Code)。
  • 前导符是在对磁盘进行格式化的时候写上去的,它包括柱面数和扇区号,扇区大小以及类似 的数据,此外还包含同步信息。
  • 控制器的任务是把串行的位流转换为字节块,并进行必要的错误校正工作字节块通常会在控制器内部 的一个缓冲区按位进行组装,然后再对校验和进行校验并证明字节块没有错误后,再将它复制到内存 中

内存映射I/O

  • 每个控制器都会有几个寄存器用来和CPU进行通信。通过写入这些寄存器,操作系统可以命令设备发 送数据,接收数据、开启或者关闭设备等。通过从这些寄存器中读取信息,操作系统能够知道设备的状 态,是否准备接受一个新命令等
  • 为了控制寄存器,许多设备都会有数据缓冲区(data buffer),来供系统进行读写。
  • 例如,在屏幕 上显示一个像素的常规方法是使用一个视频RAM,这一RAM基本上只是一个数据缓冲区,用来供程 序和操作系统写入数据。
  • 那么问题来了,CPU如何与设备寄存器和设备数据缓冲区进行通信呢?存在两个可选的方式。
  • 第一种 方法是,每个控制寄存器都被分配一个I/O端口(I/O port)号,这是一个8位或16位的整数。所 有I/O端口的集合形成了受保护的I/O端口空间,以便普通用户程序无法访问它(只有操作系统可以访 问)。使用特殊的I/O指令像是
IN REG,PORT
  • CPU可以读取控制寄存器PORT的内容并将结果放在CPU寄存器REG中。类似的,使用
OUT PORT,REG
  • CPU可以将REG的内容写到控制寄存器中
  • 大多数早期计算机,包括几乎所有大型主机,如旧M 360 及其所有后续机型,都是以这种方式工作的。
  • 控制寄存器是一个处理器寄存器而改变或控制的一般行为CPU或其他数字设备。控制寄存器执行 的常见任务包括中断控制,切换寻址模式,分页控制和协处理器控制

  • 在这一方案中,内存地址空间和I/O地址空间是不相同的,如下图所示
    在这里插入图片描述

  • 指令IN R0,4MOV R0,4这一设计中完全不同。前者读取I/O端口 4的内容并将其放入R0,而后者读取存储器字4的内容并将其放入R0,这些示例中的4代表不同且不相关的地址空间。

  • 第二个方法是PDP-11引入的
    在这里插入图片描述

  • 它将所有控制寄存器映射到内存空间中,如下图所示:
    在这里插入图片描述

  • 内存映射的I/O是在CPU与其连接的外围设备之间交换数据和指令的一种方式,这种方式是处理器 和IO设备共享同一内存位置的内存,即处理器和IO设备使用内存地址进行映射。

  • 在大多数系统中,分配给控制寄存器的地址位于或者靠近地址的顶部附近

下面是采用的一种混合方式:
在这里插入图片描述

  • 这种方式具有与内存映射I/O的数据缓冲区,而控制寄存器则具有单独的I/O端口
  • X86采用这一体系 结构。在旧M PC兼容机中,除了 0到64K - 1的I/O端口之外,640 K到1M - 1的内存地址保留给 设备的数据缓冲区。
  • 这些方案是如何工作的呢?当CPU想要读入一个字的时候,无论是从内存中读入还是从I/O端口读 入,它都要将需要的地址放到总线地址线上,然后在总线的一条控制线上调用一个READ信号。
  • 还有 第二条信号线来表明需要的是I/O空间还是内存空间如果是内存空间,内存将响应请求。如果是I/O 空间,那么I/O设备将响应请求
  • 如果只有内存空间,那么每个内存模块和每个I/O设备都会将地址线 和它所服务的地址范围进行比较。如果地址落在这一范围之内,它就会响应请求。绝对不会出现地址既 分配给内存又分配给I/O设备,所以不会存在歧义和冲突。

内存映射I/O的优点和缺点

  • 这两种寻址控制器的方案具有不同的优缺点,先来看一下内存映射I/O的优点。
  • •第一,如果需要特殊的I/O指令读写设备控制寄存器,那么访问这些寄存器需要使用汇编代码,因 为在C或C++中不存在执行IN和OUT指令的方法。调用这样的过程增加了 I/O的开销。在 内存映射中,控制寄存器只是内存中的变量,在C语言中可以和其他变量一样进行寻址。
  • 第二,对于内存映射I/O ,不需要特殊的保护机制就能够阻止用户进程执行I/O操作。操作系统需 要保证的是禁止把控制寄存器的地址空间放在用户的虚拟地址中就可以了
  • 第三,对于内存映射I/O,可以引用内存的每一条指令也可以引用控制寄存器,便于引用
  • 在计算机设计中,几乎所有的事情都要权衡。内存映射I/O也是一样,它也有自己的缺点:
  • 首先,大部 分计算机现在都会有一些对于内存字的缓存。缓存一个设备控制寄存器的代价是很大的。为了避免这种 内存映射I/O的情况,硬件必须有选择性的禁用缓存
  • 例如,在每个页面上禁用缓存,这个功能为硬件 和操作系统增加了额外的复杂性,因此必须选择性的进行管理。
  • 第二点,如果仅仅只有一个地址空间,那么所有的内存模块(memory modules)和所有的I/O设备都 必须检查所有的内存引用来推断出谁来进行响应
  • 什么是内存模块?在计算中,存储器模块是其上安装有存储器集成电路的印刷电路板

在这里插入图片描述
在这里插入图片描述

  • 让每个内存模块和I/O设备查看每个地址是简单易行的。然而,现代个人计算机的趋势是专用的高速内存总线

如下图所示:
在这里插入图片描述

  • 装备这一总线是为了优化内存访问速度,X86系统还可以有多种总线(内存、PCIe. SCSI和USB)

如下图所示:
在这里插入图片描述

  • 内存映射机器上使用单独的内存总线的麻烦之处在于,I/O设备无法通过内存总线查看内存地址,因 此它们无法对其进行响应
  • 此外,必须采取特殊的措施使内存映射I/O工作在具有多总线的系统上
  • 一 种可能的 方法是首先将全部内存引用发送到内存,如果内存响应失败,CPU再尝试其他总线
  • 第二种设计是在内存总线上放一个探查设备,放过所有潜在指向所关注的I/O设备的地址。此处的问 题是,I/O设备可能无法以内存所能达到的速度处理请求
  • 第三种可能的设计是在内存控制器中对地址进行过滤,这种设计与上图所描述的设计相匹配。这种情况 下,内存控制器芯片中包含在引导时预装载的范围寄存器。
  • 这一设计的缺点是需要在引导时判定哪些内 存地址而不是真正的内存地址。因而,每一设计都有支持它和反对它的论据,所以折中和权衡是不可避 免的

直接内存访问

  • 无论一个CPU是否具有内存映射I/O,它都需要寻址设备控制器以便与它们交换数据
  • CPU可以从 I/O控制器每次请求一个字节的数据,但是这么做会浪费CPU时间,所以经常会用到一种称为直接内 存访问(Direct Memory Access)的方案。
  • 为了简化,我们假设CPU通过单一的系统总线访问所有 的设备和内存,该总线连接CPU、内存和I/O设备

如下图所示:
在这里插入图片描述

  • 现代操作系统实际更为复杂,但是原理是相同的。如果硬件有DMA控制器,那么操作系统只能使用 DMA
  • 有时这个控制器会集成到磁盘控制器和其他控制器中,但这种设计需要在每个设备上都装有一个 分离的DMA控制器。单个的DMA控制器可用于向多个设备传输,这种传输往往同时进行
  • 不管DMA控制器的物理地址在哪,它都能够独立于CPU从而访问系统总线,如上图所示。它包含几 个可由CPU读写的寄存器,其中包括一个内存地址寄存器,字节计数寄存器和一个或多个控制寄存 器。
  • 控制寄存器指定要使用的I/O端口、传送方向(从I/O设备读或写到I/O设备)、传送单位(每次 —个字节或者每次一个字)以及在一次突发传送中要传送的字节数
  • 为了解释DMA的工作原理,我们首先看一下不使用DMA该如何进行磁盘读取:
  • •首先,控制器从磁盘驱动器串行地、一位一位的读一个块(一个或多个扇区),直到将整块信息 放入控制器的内部缓冲区
  • 读取校验和以保证没有发生读错误。然后控制器会产生一个中断,当操作系统开始运行时,它会 重复的从控制器的缓冲区中一次一个字节或者一个字地读取该块的信息,并将其存入内存中。

DMA工作原理

当使用DMA后,这个过程就会变得不一样了。

  • 首先CPU通过设置DMA控制器的寄存器对它进行编 程,所以DMA控制器知道将什么数据传送到什么地方
  • DMA控制器还要向磁盘控制器发出一个命 令,通知它从磁盘读数据到其内部的缓冲区并检验校验和。当有效数据位于磁盘控制器的缓冲区中时, DMA就可以开始了。
  • DMA控制器通过在总线上发出一个读请求到磁盘控制器而发起DMA传送,这是第二步
  • 这个读请求 就像其他读请求一样,磁盘控制器并不知道或者并不关心它是来自CPU还是来自DMA控制器。
  • 通常 情况下,要写的内存地址在总线的地址线上,所以当磁盘控制器去匹配下一个字时,它知道将该字写到 什么地方。写到内存就是另外一个总线循环了,这是第三步
  • 写操作完成时,磁盘控制器在总线上发 出一个应答信号到DMA控制器,这是第四步。
  • 然后,DMA控制器会增加内存地址并减少字节数量。如果字节数量仍然大于0,就会循环步骤2-步 骤4 ,直到字节计数变为0
  • 此时,DMA控制器会打断CPU并告诉它传输已经完成了。操作系统开 始运行时,它不会把磁盘块拷贝到内存中,因为它已经在内存中了。
  • 不同DMA控制器的复杂程度差别很大。最简单的DMA控制器每次处理一次传输,就像上面描述的那 样。
  • 更为复杂的情况是一次同时处理很多次传输,这样的控制器内部具有多组寄存器,每个通道一组寄 存器
  • 在传输每一个字之后,DMA控制器就决定下一次要为哪个设备提供服务。DMA控制器可能被设 置为使用 轮询算法,或者它也有可能具有一个优先级规划设计,以便让某些设备受到比其他设备更多 的照顾
  • 假如存在一个明确的方法分辨应答信号,那么在同一时间就可以挂起对不同设备控制器的多个 请求。
  • 许多总线能够以两种模式操作:每次一字模式和块模式。一些DMA控制器也能够使用这两种方式进行 操作。
  • 在前一个模式中,DMA控制器请求传送一个字并得到这个字。如果CPU想要使用总线,它必须 进行等待。设备可能会偷偷进入并且从CPU偷走一个总线周期,从而轻微的延迟CPU。这种机制称为 周期窃取(cycle stealing)。
  • 在块模式中,DMA控制器告诉设备获取总线,然后进行一系列的传输操作,然后释放总线。这一操作 的形式称为 突发模式( burst mode)
  • 这种模式要比周期窃取更有效因为获取总线占用了时间,并且 —次总线获得的代价是可以同时传输多个字。缺点是如果此时进行的是长时间的突发传送,有可能将 CPU和其他设备阻塞很长的时间
  • 在我们讨论的这种模型中,有时被称为飞越模式(fly-by mode) , DMA控制器会告诉设备控制器把 数据直接传递到内存。一些DMA控制器使用的另一种模式是让设备控制器将字发送给DMA控制器
  • 然后DMA控制器发出第二条总线请求,将字写到任何可以写入的地方。采用这种方案,每个传输的字 都需要一个额外的总线周期,但是更加灵活,因为它还可以执行设备到设备的复制,甚至是内存到内存 的复制(通过事先对内存进行读取,然后对内存进行写入)。
  • 大部分的DMA控制器使用物理地址进行传输。使用物理地址需要操作系统将目标内存缓冲区的虚拟地 址转换为物理地址,并将该物理地址写入DMA控制器的地址寄存器中。
  • 另一种方案是一些DMA控制 器将虚拟地址写入DMA控制器中。然后,DMA控制器必须使用MMU才能完成虚拟到物理的转换。 仅当MMU是内存的一部分而不是CPU的一部分时,才可以将虚拟地址放在总线上

中断

  • 在一台个人计算机体系结构中,中断结构会如下所示:
    在这里插入图片描述
  • 一个I/O设备完成它的工作后,它就会产生一个中断(默认操作系统已经开启中断),它通过在总线 上声明已分配的信号来实现此目的。主板上的中断控制器芯片会检测到这个信号,然后执行中断操作
  • 如果在中断前没有其他中断操作阻塞的话,中断控制器将立刻对中断进行处理,如果在中断前还有其他 中断操作正在执行,或者有其他设备发出级别更高的中断信号的话,那么这个设备将暂时不会处理。 在这种情况下,该设备会继续在总线上置起中断信号,直到得到CPU服务。
  • 为了处理中断,中断控制器在地址线上放置一个数字,指定要关注的设备是哪个,并声明一个信号以中 断CPU
  • 中断信号导致CPU停止当前正在做的工作并且开始做其他事情。地址线上会有一个指向中 断向量表的索引,用来获取下一个程序计数器。这个新获取的程序计数器也就表示着程序将要开始, 它会指向程序的开始处
  • 一般情况下,陷阱和中断从这一点上看使用相同的机制,并且常常共享相同的 中断向量。中断向量的位置可以硬连线到机器中,也可以位于内存中的任何位置,由CPU寄存器指向 其起点
  • 中断服务程序开始运行后,中断服务程序通过将某个值写入中断控制器的I/O端口来确认中断。告诉它 中断控制器可以自由地发出另一个中断
  • 通过让CPU延迟响应来达到多个中断同时到达CPU涉及到 竞争的情况发生。一些老的计算机没有集中的中断控制器,通常每个设备请求自己的中断。
  • 硬件通常在服务程序开始前保存当前信息。对于不同的CPU来说,哪些信息需要保存以及保存在哪里 差别很大。不管其他的信息是否保存,程序计数器必须要被保存,这对所有的CPU来说都是相同的, 以此来恢复中断的进程。所有可见寄存器和大量内部寄存器也应该被保存
  • 上面说到硬件应该保存当前信息,那么保存在哪里是个问题:
  • 一种选择是将其放入到内部寄存器中,在 需要时操作系统可以读出这些内部寄存器。这种方法会造成的问题是:一段时间内设备无法响应,直到 所有的内部寄存器中存储的信息被读出后,才能恢复运行,以免第二个内部寄存器重写内部寄存器的状 态。
  • 第二种方式是在堆栈中保存信息,这也是大部分CPU所使用的方式。但是,这种方法也存在问题,因 为使用的堆栈不确定,如果使用的是当前堆栈,则它很可能是用户进程的堆栈。堆栈指针甚至不合 法,这样当硬件试图在它所指的地址处写入时,将会导致致命错误。
  • 如果使用的是内核堆栈,堆栈指针 是合法的并且指向一个固定的页面,这样的机会可能会更大。然而,切换到内核态需要切换MMU上下 文,并且可能使高速缓存或者TLB失效。静态或动态重新装载这些东西将增加中断处理的时间,浪费 CPU时间。

精确中断和不精确中断

另一个问题是:现代CPU大量的采用流水线并且有时还采用超标量(内部并行)。

  • 在一些老的系统 中,每条指令执行完毕后,微程序或硬件将检查是否存在未完成的中断。如果存在,那么程序计数器和 PSW将被压入堆栈中开始中断序列。在中断程序运行之后,旧的PSW和程序计数器将从堆栈中弹出 恢复先前的进程

下面是一个流水线模型:
在这里插入图片描述

  • 流水线满的时候出现一个中断会发生什么情况

  • 许多指令正处于不同的执行阶段,中断出现时,程序 计数器的值可能无法正确地反应已经执行过的指令和尚未执行的指令的边界。

  • 事实上,许多指令可能部 分执行力,不同的指令完成的程度或多或少。在这种情况下,程序计数器更有可能反应的是将要被取 出并压入流水线的下一条指令的地址,而不是刚刚被执行单元处理过的指令的地址

  • 在超标量的设计中,可能更加糟糕
    在这里插入图片描述

  • 每个指令都可以分解成为微操作,微操作有可能乱序执行,这取决于内部资源(如功能单元和寄存器) 的可用性

  • 中断发生时,某些很久以前启动的指令可能还没开始执行,而最近执行的指令可能将要马 上完成。在中断信号出现时,可能存在许多指令处于不同的完成状态,它们与程序计数器之间没有什么 关系

  • 使机器处于良好状态的中断称为精确中断(precise interrupt) ,这样的中断具有四个属性:

  • PC (程序计数器)保存在一个已知的地方

  • PC所指向的指令之前所有的指令已经完全执行

  • PC所指向的指令之后所有的指令都没有执行

  • •PC所指向的指令的执行状态是已知的

  • [] 不满足以上要求的中断称为不精确中断(imprecise interrupt),不精确中断让人很头疼。

  • 上图描 述了不精确中断的现象。指令的执行时序和完成度具有不确定性,而且恢复起来也非常麻烦。

猜你喜欢

转载自blog.csdn.net/wolfGuiDao/article/details/107761178