计算机最魔幻的事情就是它能感知到你的思想

计算机最魔幻的事情就是它能感知到你的思想

咱们以前的文章提到了操做系统的三个抽象,它们分别是进程、地址空间和文件,除此以外,操做系统还要控制全部的 I/O 设备。操做系统必须向设备发送命令,捕捉中断并处理错误。它还应该在设备和操做系统的其他部分之间提供一个简单易用的接口。操做系统如何管理 I/O 是咱们接下来的重点。程序员

不一样的人对 I/O 硬件的理解也不一样。对于电子工程师而言,I/O 硬件就是芯片、导线、电源和其余组成硬件的物理设备。而咱们程序员眼中的 I/O 其实就是硬件提供给软件的接口,好比硬件接受到的命令、执行的操做以及反馈的错误。咱们着重探讨的是如何对硬件进行编程,而不是其工做原理。算法

I/O 设备
什么是 I/O 设备?I/O 设备又叫作输入/输出设备,它是人类用来和计算机进行通讯的外部硬件。输入/输出设备可以向计算机发送数据(输出)并从计算机接收数据(输入)。编程

I/O 设备(I/O devices)能够分红两种:块设备(block devices) 和 字符设备(character devices)。缓存

块设备
块设备是一个能存储固定大小块信息的设备,它支持以固定大小的块,扇区或群集读取和(可选)写入数据。每一个块都有本身的物理地址。一般块的大小在 512 - 65536 之间。全部传输的信息都会以连续的块为单位。块设备的基本特征是每一个块都较为对立,可以独立的进行读写。常见的块设备有 硬盘、蓝光光盘、USB 盘网络

与字符设备相比,块设备一般须要较少的引脚。
计算机最魔幻的事情就是它能感知到你的思想electron

块设备的缺点ide

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

字符设备
另外一类 I/O 设备是字符设备。字符设备以字符为单位发送或接收一个字符流,而不考虑任何块结构。字符设备是不可寻址的,也没有任何寻道操做。常见的字符设备有 打印机、网络设备、鼠标、以及大多数与磁盘不一样的设备。操作系统

计算机最魔幻的事情就是它能感知到你的思想

下面显示了一些常见设备的数据速率。设计

计算机最魔幻的事情就是它能感知到你的思想

设备控制器

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

设备控制器是处理 CPU 传入和传出信号的系统。设备经过插头和插座链接到计算机,而且插座链接到设备控制器。设备控制器从链接的设备处接收数据,并将其存储在控制器内部的一些特殊目的寄存器(special purpose registers) 也就是本地缓冲区中。

特殊用途寄存器,顾名思义是仅为一项任务而设计的寄存器。例如,cs,ds,gs 和其余段寄存器属于特殊目的寄存器,由于它们的存在是为了保存段号。eax,ecx 等是通常用途的寄存器,由于你能够无限制地使用它们。例如,你不能移动 ds,可是能够移动 eax,ebx。通用目的寄存器好比有:eax、ecx、edx、ebx、esi、edi、ebp、esp特殊目的寄存器好比有:cs、ds、ss、es、fs、gs、eip、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 的内容写到控制寄存器中。大多数早期计算机,包括几乎全部大型主机,如 IBM 360 及其全部后续机型,都是以这种方式工做的。

控制寄存器是一个处理器寄存器而改变或控制的通常行为 CPU 或其余数字设备。控制寄存器执行的常见任务包括中断控制,切换寻址模式,分页控制和协处理器控制。”
在这一方案中,内存地址空间和 I/O 地址空间是不相同的,以下图所示
计算机最魔幻的事情就是它能感知到你的思想

指令

IN R0,4

MOV R0,4

这一设计中彻底不一样。前者读取 I/O端口 4 的内容并将其放入 R0,然后者读取存储器字 4 的内容并将其放入 R0。这些示例中的 4 表明不一样且不相关的地址空间。

第二个方法是 PDP-11 引入的,

什么是 PDP-11?

计算机最魔幻的事情就是它能感知到你的思想

它将全部控制寄存器映射到内存空间中,以下图所示

计算机最魔幻的事情就是它能感知到你的思想

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

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

下面是采用的一种混合方式

计算机最魔幻的事情就是它能感知到你的思想

这种方式具备与内存映射 I/O 的数据缓冲区,而控制寄存器则具备单独的 I/O 端口。x86 采用这一体系结构。在 IBM 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),不精确中断让人很头疼。上图描述了不精确中断的现象。指令的执行时序和完成度具备不肯定性,并且恢复起来也很是麻烦。
相关文章
相关标签/搜索