操作系统还要控制计算机的所有I/O设备。它为设备和系统之间提供简单易用的接口。如果有可能,这个接口对于所有设备都应该是相同的,这就是所谓的设备无关性。I/O部分的代码是整个操作系统的重要组成部分。
I/O设备大致可以分为两类:块设备和字符设备。前者传输以一个块为单位,硬盘、CD-ROM、USB都是常见的块设备。后者以字符为单位发送或接收一个字符流,如:打印机、网络接口、鼠标等。
I/O设备一般由机械部件和电子部件两部分组成。电子部件称为设备控制器或适配器,在个人计算机上,它经常以主板上的芯片的形式出现(如PCI等)。控制器卡上通常有一个连接器,通向设备本身的电缆可以插入到这个连接器中。控制器与设备之前的接口通常是一个很低层次的接口。包括处理前导符和错误校正码(ECC)。前导符是在对磁盘进行格式化时写上去的,它包括柱面数和扇区号、扇区大小等。控制器的任务是把串行的位流转换为字节块,并进行必要的错误校正工作,再将复制到主存中。那么控制器如何与CPU进行通信呢?
一种是独立编址,采用单独的I/O和内存空间,每个控制寄存器被分配一个I/O端口,这是一个8位或16位的整数。所有I/O端口形成I/O端口空间,并且受到保护使得普通的用户程序不能对其进行访问。使用一些特殊的I/O指令。一种是统一的编址(称为内存映射I/O),就是每个控制器被分配惟一的一个内存地址,并且不会有内存被分配这一地址。这里主要谈谈两者的区别。
如果采用第一种方式编程,那么需要用到特殊的I/O指令,就要用到汇编语言,但是在C和C++中没有这样的系统调用,所以需要增加控制代码,而第二种就没有这样的顾虑。对于内存映射I/O,不需要特别的保护机制来阻止用户进程执行I/O操作;可以引用内存的每一条指令也可以引用控制寄存器。关于内存映射I/O的缺点就是不能使用高速缓存的能力,不然将导致灾难性的问题发生。另外就是关于地址空间问题,如果是单一总线还简单易行,但是现在大多数的个人计算机都是包含一些专用的高速总线,这样在I/O设备将无法检测内存地址空间是否可用。
直接存储器存取
无论一个CPU是否具有内存映射I/O,它都需要寻址设备控制器以便与它们交换数据。CPU可以从I/O控制器每次请求一个字节的数据,但是这样做浪费CPU的实际,所以经常用到了一种称为直接存储器存取(DMA),采用此方式,那么可以达到CPU和I/O处理并行的效果。关于DMA的工作方式很简单,这里就不进行总结了。DMA控制器在复杂性方面的区别相当大。有的可以处理一路传送,有的可以处理多路传送。而且大多数的DMA控制器使用物理内存地址进行传送。使用物理地址要求操作系统将预期的内存缓冲区的虚拟地址转换为物理地址,并且将该物理地址写入到DMA控制器的地址寄存器中。
在DMA开始之前,磁盘首先要将数据读入其内部的缓冲区中。你也许会产生疑问:为什么控制器从磁盘读取字节后不立即将其存储在主存中?这里有两个原因,第一是检验校验和。第二是一旦磁盘传送开始工作,从磁盘读出的数据就是以固定速率到达的,而不论控制器是否准备好接收数据。如果控制器要将数据直接写到内存,则它必须为要传送的每个字取得系统总线的控制权,此时,若由于其他设备使用总线而导致总线忙,则控制器只能等待。那么磁盘传送过来的数据就需要先保存在一个缓冲区里,防止数据丢失。
跟I/O操作相关还有一个概念就是中断机制。(该机制也很简单这里也不进行总结了),下面看看I/O的软件原理。
首先说明I/O软件的目标:
1)设备独立性
2)统一命名
3)错误处理
4)同步与异步
5)缓冲
6)共享设备和独享设备
I/O可以以三种根本不同的方式实现。第一种是程序控制I/O,这种就是让CPU做全部的工作(包括I/O处理),大多数程序也会这样编写。这种机制操作简单,但是CPU使用效率低,另外容易导致轮询或忙等待。第二种是中断驱动I/O,这种方式运行CPU在等待打印机变为就绪的同时做某些其他事情的方式就是使用中断。假设我们考虑不使用缓冲区的方式打印字符,那么中断有一个明显的缺点是中断发生在每个字符上。中断要花费时间,所以这一方法将浪费一定数量的CPU时间。那么第三种方式将解决这样的问题,就是DMA方式。
I/O软件层次
I/O软件分为四层。如下表:
用户级I/O软件 |
与设备无关的操作系统软件 |
设备驱动程序 |
中断处理程序 |
硬件 |
关于中断处理程序,是当中断发生时,中断处理程序将做它必须要做的全部工作以便对中断进行处理。包括设置相关寄存器(PC等)、上下文设置、MMU、TLB、堆栈、设备状态、中断判优等。
关于设备驱动程序,每个控制器都设有某些设备寄存器用来向设备发出命令,或者设有某些设备寄存器用来读出设备的状态,或者设有这两种设备寄存器。因而,每个连接到计算机上的I/O设备都需要某些设备特定的代码来对其进行控制。这样的代码称为设备驱动程序。它具有相关功能,最明显的功能是接收来自其上方与设备无关的软件所发出的抽象的读写请求,并且目睹这些请求被执行。接着是检查设备当前是否在使用。对相关命令的解析处理。
关于与设备无关的I/O软件,具有以下功能:
设备驱动程序的统一接口 |
缓冲 |
错误报告 |
分配与释放专用设备 |
提供与设备无关的块大小 |
关于用户级的I/O软件,这块就是大家开发过程中,经常使用到的库,I/O的调用都是由这些库实现。最简单的就是read方法、write方法等。另外是数据格式化等。并非所有的用户层I/O软件都是由库过程组成的,另一个重要的类比就是假脱机系统。假脱机系统是多道程序设计系统中处理独占I/O设备的一种方法。典型的假脱机设备:打印机。另一种方法是创建一个特殊进程,称为守护进程,以及一个特殊目录,称为假脱机目录。该方式可以解决某些进程不必要地长期空占打印机的问题。
下面主要介绍盘,(包括光盘和硬盘等),对磁盘驱动程序有重要意义的一个设备特性是:控制器是否可以同时控制两个或多个驱动器进行寻道,这就是重叠寻道。第一个是区分物理几何规格和虚拟几何规格。前者是外层比内层拥有更多的扇区,为了隐藏每个磁道有多少个扇区的细节,大多数现代磁盘都有一个虚拟几何规格呈现给操作系统。关于RAID(廉价磁盘冗余阵列),其基本思想就是将一个装满了磁盘的盒子安装到计算机上,用RAID控制器替代磁盘控制器卡,将数据复制到整个RAID上,然后继续常规的操作。关于光盘和磁盘有一个重要的区别是:前者是恒定线速度驱动器,后者是恒定角速度驱动器。关于磁盘还有一个重要的概念是磁盘格式化,磁盘进行格式化时,会存在一个偏移,这个偏移叫柱面斜进。这样做的目的是为了改进性能。还有就是磁盘臂调度算法,关于读/写一个磁盘块需要多长时间,由三个因素决定:1)寻道时间,2)旋转延迟,3)实际数据传输时间。不过第一点占据主导地位,所有减少平均寻道时间可以充分地改善系统性能。相关算法包括FCFS算法,SSF算法和电梯算法。
关于磁盘的错误处理,一个是关于坏道,另一个关于稳定存储器(要么正确写数据,要么什么也不做),那么稳定的存储器需要使用一对完全相同的磁盘,对应的块一同工作以形成一个无差错的块。当不存在错误时,在两个驱动器上对应的块是相同的,读取任意一个都可以得到相同的结果,为了到达这一目的,定义三个操作:1)稳定写,2)稳定读,3)崩溃恢复。