发送命令
,捕捉中断
并处理错误
。它还应该在设备和操作系统的其余部分之间提供一个简单易用的接口。操作系统如何管理 I/O 是我们接下来的重点。接口
,比如硬件接受到的命令、执行的操作以及反馈的错误。我们着重探讨的是如何对硬件进行编程,而不是其工作原理。发送数据(输出)
并从计算机接收数据(输入)
。I/O 设备(I/O devices)
可以分成两种:块设备(block devices)
和 字符设备(character devices)
。固定大小块
信息的设备,它支持以固定大小的块,扇区或群集读取和(可选)写入数据。每个块都有自己的物理地址
。通常块的大小在 512 - 65536 之间。所有传输的信息都会以连续
的块为单位。块设备的基本特征是每个块都较为对立,能够独立的进行读写。常见的块设备有 硬盘、蓝光光盘、USB 盘字符设备
。字符设备以字符
为单位发送或接收一个字符流,而不考虑任何块结构。字符设备是不可寻址的,也没有任何寻道操作。常见的字符设备有 打印机、网络设备、鼠标、以及大多数与磁盘不同的设备。特殊目的寄存器(special purpose registers)
也就是本地缓冲区中。机械组件(mechanical component)
和电子组件(electronic component)
构成。电子组件被称为 设备控制器(device controller)
或者 适配器(adapter)
。在个人计算机上,它通常采用可插入(PCIe)扩展插槽
的主板上的芯片或印刷电路卡的形式。前导符(preamble)
开始,然后是一个扇区中的 4096 位,最后是一个校验和
或 ECC(错误码,Error-Correcting Code)
。前导符是在对磁盘进行格式化的时候写上去的,它包括柱面数和扇区号,扇区大小以及类似的数据,此外还包含同步信息。寄存器
,许多设备都会有数据缓冲区(data buffer)
,来供系统进行读写。例如,在屏幕上显示一个像素的常规方法是使用一个视频 RAM,这一 RAM 基本上只是一个数据缓冲区,用来供程序和操作系统写入数据。I/O 端口(I/O port)
号,这是一个 8 位或 16 位的整数。所有 I/O 端口的集合形成了受保护的 I/O 端口空间,以便普通用户程序无法访问它(只有操作系统可以访问)。使用特殊的 I/O 指令像是OUT PORT,REG
MOV R0,4
内存映射的 I/O
是在 CPU 与其连接的外围设备之间交换数据和指令的一种方式,这种方式是处理器和 IO 设备共享同一内存位置
的内存,即处理器和 IO 设备使用内存地址进行映射。READ
信号。还有第二条信号线来表明需要的是 I/O 空间还是内存空间。如果是内存空间,内存将响应请求。如果是 I/O 空间,那么 I/O 设备将响应请求。如果只有内存空间,那么每个内存模块和每个 I/O 设备都会将地址线和它所服务的地址范围进行比较。如果地址落在这一范围之内,它就会响应请求。绝对不会出现地址既分配给内存又分配给 I/O 设备,所以不会存在歧义和冲突。IN
和 OUT
指令的方法。调用这样的过程增加了 I/O 的开销。在内存映射中,控制寄存器只是内存中的变量,在 C 语言中可以和其他变量一样进行寻址。内存模块(memory modules)
和所有的 I/O 设备都必须检查所有的内存引用来推断出谁来进行响应。探查设备
,放过所有潜在指向所关注的 I/O 设备的地址。此处的问题是,I/O 设备可能无法以内存所能达到的速度处理请求。直接内存访问(Direct Memory Access)
的方案。为了简化,我们假设 CPU 通过单一的系统总线访问所有的设备和内存,该总线连接 CPU 、内存和 I/O 设备,如下图所示DMA 控制器
,那么操作系统只能使用 DMA。有时这个控制器会集成到磁盘控制器和其他控制器中,但这种设计需要在每个设备上都装有一个分离的 DMA 控制器。单个的 DMA 控制器可用于向多个设备传输,这种传输往往同时进行。磁盘驱动器
串行地、一位一位的读一个块(一个或多个扇区),直到将整块信息放入控制器的内部缓冲区。校验和
以保证没有发生读错误。然后控制器会产生一个中断,当操作系统开始运行时,它会重复的从控制器的缓冲区中一次一个字节或者一个字地读取该块的信息,并将其存入内存中。读请求
到磁盘控制器而发起 DMA 传送,这是第二步。这个读请求就像其他读请求一样,磁盘控制器并不知道或者并不关心它是来自 CPU 还是来自 DMA 控制器。通常情况下,要写的内存地址在总线的地址线上,所以当磁盘控制器去匹配下一个字时,它知道将该字写到什么地方。写到内存就是另外一个总线循环了,这是第三步。当写操作完成时,磁盘控制器在总线上发出一个应答信号到 DMA 控制器,这是第四步。轮询算法
,或者它也有可能具有一个优先级规划设计,以便让某些设备受到比其他设备更多的照顾。假如存在一个明确的方法分辨应答信号,那么在同一时间就可以挂起对不同设备控制器的多个请求。周期窃取(cycle stealing)
。突发模式(burst mode)
。这种模式要比周期窃取更有效因为获取总线占用了时间,并且一次总线获得的代价是可以同时传输多个字。缺点是如果此时进行的是长时间的突发传送,有可能将 CPU 和其他设备阻塞很长的时间。飞越模式(fly-by mode)
,DMA 控制器会告诉设备控制器把数据直接传递到内存。一些 DMA 控制器使用的另一种模式是让设备控制器将字发送给 DMA 控制器,然后 DMA 控制器发出第二条总线请求,将字写到任何可以写入的地方。采用这种方案,每个传输的字都需要一个额外的总线周期,但是更加灵活,因为它还可以执行设备到设备的复制,甚至是内存到内存的复制(通过事先对内存进行读取,然后对内存进行写入)。正在执行
,或者有其他设备发出级别更高
的中断信号的话,那么这个设备将暂时不会处理。在这种情况下,该设备会继续在总线上置起中断信号,直到得到 CPU 服务。中断向量表
的索引,用来获取下一个程序计数器。这个新获取的程序计数器也就表示着程序将要开始,它会指向程序的开始处。一般情况下,陷阱和中断从这一点上看使用相同的机制,并且常常共享相同的中断向量。中断向量的位置可以硬连线到机器中,也可以位于内存中的任何位置,由 CPU 寄存器指向其起点。当前堆栈
,则它很可能是用户进程的堆栈。堆栈指针甚至不合法,这样当硬件试图在它所指的地址处写入时,将会导致致命错误。如果使用的是内核堆栈,堆栈指针是合法的并且指向一个固定的页面,这样的机会可能会更大。然而,切换到内核态需要切换 MMU 上下文,并且可能使高速缓存或者 TLB 失效。静态或动态重新装载这些东西将增加中断处理的时间,浪费 CPU 时间。流水线
并且有时还采用超标量(内部并行)
。在一些老的系统中,每条指令执行完毕后,微程序或硬件将检查是否存在未完成的中断。如果存在,那么程序计数器和 PSW 将被压入堆栈中开始中断序列。在中断程序运行之后,旧的 PSW 和程序计数器将从堆栈中弹出恢复先前的进程。精确中断(precise interrupt)
。这样的中断具有四个属性:不精确中断(imprecise interrupt)
,不精确中断让人很头疼。上图描述了不精确中断的现象。指令的执行时序和完成度具有不确定性,而且恢复起来也非常麻烦。近期精彩内容推荐:
在看点这里好文分享给更多人↓↓
联系客服