操作系统应该控制计算机的所有I/O设备,必须向设备发送命令,捕捉中断,并处理设备的各种错误。在设备和系统的其他部分之间提供简单且易于使用的接口。

I/O硬件原理

I/O硬件提供给软件的接口,比如硬件能够接受的命令,完成的功能。

I/O设备

  • 块设备:信息存储在固定大小的块中,每个块有自己的地址,比如硬盘,块可寻址的设备。
  • 字符设备:以字符为单位发送或者接收一个字符流,字符设备是不可寻址的,也没有寻道操作,比如打印机

文件系统只处理抽象的块设备,而把与设备相关的部分留给较低层的软件。

设备控制器

I/O设备一般由机械部件和电子部件组成,电子部件称为设备控制器或者适配器。比如主板上的芯片,或者是插入扩展槽中的印刷电路板。

控制器与设备之间的接口是一个低层次的接口,磁盘中从驱动器出来的事一个串行的位(比特流),以一个前导符开始,接着是一个扇区的4096位,最后是一个校验和,为错误校正码。

控制器的任务是把串行的位流转换成字节块,并进行必要的校正工作,再将它复制到主存中。

内存映射I/O

每个控制器有几个寄存器来和CPU进行通信,通过写入这些寄存器,操作系统可以命令设备发送,接收数据,开启或者关闭。有些设备还有一个操作系统可以读写的数据缓冲区。CPU和设备的控制寄存器,数据缓冲区通信有两个方法

  • 每个控制寄存器被分配一个I/O端口,而且I/O地址空间和内存地址空间是不同的。
  • 将所有控制寄存器映射到内存空间中。每个控制寄存器被分配唯一的内存地址,并且不会有内存被分配这一地址,被称为内存映射I/O。或者是既有内存映射I/O的数据缓冲区,控制寄存器则具有单独的I/O端口。

CPU想要读入一个字的时候,要将需要的地址放在总线的地址线上,然后在总线的一条控制线上置起一个READ信号,使用第二条信号线来表明需要的是I/O空间还是内存空间。如果只有内存空间,那么每个内存模块和每个I/O设备都会将地址线和它所服务的地址范围进行比较,如果地址落在这一范围之内,它就会响应请求。

优点

  • 使用内存映射I/O,可以使用其他高级语言来控制内存,而不需要汇编。
  • 不需要特殊的保护机制来阻止用户进程来执行I/O操作
  • 可以引用内存的每一条指令也可以引用控制寄存器。

缺点:

  • 内存有高速缓存
  • 专用的高速内存总线,I/O设备没有办法查看内存地址,因为内存地址旁路到内存总线上,所以没有办法响应。

直接存储器存取

CPU需要寻址设备控制器以便与它们交换数据,CPU可以从I/O控制器每次请求一个字节的数据,但是这样会浪费CPU,所以使用直接存储器存取(DMA),而且必须硬件有DMA控制器。

DMA控制器可以独立于CPU访问系统总线,它包含若干个可以被CPU读写的寄存器,包括一个内存地址寄存器,一个字节计数寄存器和一个或多个控制寄存器。控制寄存器指定要使用的I/O端口,传送方向,传送单位以及在一次突发传送中要传送的字节数。

  • 无DMA控制器的读取过程

控制器从磁盘驱动器串行的,一位一位的读一个块,直到将整块信息放入控制器的内部缓冲区,然后计算校验和,保证没有错误,然后控制器产生一个中断,然后操作系统将缓冲区的数据放入内存中。

  • 有DMA控制器的读取过程

CPU通过设置DMA控制器的寄存器对它进行编程,DMA控制器就知道将什么数据传送到什么地方。DMA控制器接着向磁盘控制器发送命令,读取数据,检查校验和。

一般情况,要写的内存地址在总线的地址线上,磁盘控制器在将数据写到内存操作完成后,在总线上发出一个应答信号到DMA控制器,DMA的字节计数为0的时候,DMA产生中断CPU。操作系统开始工作,这时候数据已经在内存中了。

重温中断

当一个I/O设备完成它的工作时,就产生一个中断,是通过在分配给它的一条总线信号线上置起信号而产生中断的,该信号被主板上的中断控制器芯片检测到,由中断控制器芯片决定做什么。

为了处理中断,中断控制器在地址线上放置一个数字表明哪个设备需要关注,并且置起一个中断CPU的信号。中断信号导致CPU停止当前的工作并且开始做其他事情,地址线上的数字被用做指向一个中断向量的表格的索引,读取一个新的程序计数器,这个程序计数器指向相应中断服务过程的开始。程序计数器保证了中断的进程可以重新开始,用于存放下一条指令所在单元的地址的地方。

将机器留在一个明确状态的中断称为精确中断:

  • PC(程序计数器)保存在一个已知的地方
  • PC所指向的指令之前的所有指令已经完全执行
  • PC指向的指令之后的所有指令都没有执行
  • PC指向的指令的执行状态是已知的

不符合上面条件的是不精确中断,通常需要将大量的内部状态放到堆栈总,从而使得操作系统有可能判断出正在发生什么事情,每次中断发生时候将大量的信息保存在内存中使得中断响应十分缓慢。

I/O软件原理

I/O软件的目标

设备独立性:IO软件可以访问任意IO设备而无需事先指定设备。

  • 统一命名
  • 错误处理
  • 同步和异步(中断驱动)传输,大部分物理I/O是异步的,CPU启动传输之后便转去做其他工作,直到中断发生。操作系统使得实际上是中断启动的操作变为在用户程序看来是阻塞式的操作
  • 缓冲
  • 共享设备和独占设备,可能死锁

程序控制IO

IO最简单的形式是让CPU做全部工作,即程序控制IO。

打印机可以使用时候,操作系统就复制第一个字符到打印机的数据寄存器中,这里使用了内存映射IO,这样将激活打印机。后面就要不断查询设备以了解它是否就绪准备接收另一个字符,这一操作被称为轮询或者忙等待。

这样直到全部IO完成之前要占用CPU全部时间。

中断驱动IO

当打印机将字符打印完并且准备好接收下一个字符时候,将产生一个中断,中断会停止当前进程并且保存其状态,然后,打印机中断服务过程将运行。如果没有字符打印,中断处理程序将采取某个操作将用户进程接触阻塞,否则,输出字符,应答中断,并且返回到中断之前正在运行的进程。

DMA的IO

让DMA控制器一次给打印机提供一个字符,而不必使用CPU。DMA是将中断的次数从打印每个字符一次减少到打印每个缓冲区一次。

IO软件层次

  • 用户级IO软件
  • 与设备无关的操作系统软件
  • 设备驱动程序
  • 中断处理程序
  • 硬件

中断处理程序

隐藏中断的办法是将 启动一个IO操作的驱动程序 阻塞起来,直到IO操作完成且产生一个中断。驱动程序阻塞自己的手段有:在一个信号量上执行down操作,在一个条件变量上执行wait操作,在一个消息上执行receive操作或者某些类似的操作。

中断发生的时候,中断处理程序将启动中断的驱动程序解除阻塞,比如在信号量上执行up操作,或者对管程中的条件变量执行signal操作,或者是向被阻塞的驱动程序发一个消息。最终结果是先前被阻塞的驱动程序现在继续运行。

设备驱动程序

每个连接到计算机上的IO设备都需要某些设备特定的代码来对其进行控制,即设备驱动程序。为了访问设备的硬件(设备控制器的寄存器),设备驱动程序必须是操作系统内核的一部分。如果构造运行在用户空间的驱动程序,这样就可以防止驱动程序干扰内核。

设备驱动程序接收上方与设备无关的软件所发出的抽象读写请求,或者对设备进行初始化,电源需求和日志事件进行管理。

驱动程序必须是重入的。

与设备无关的IO软件

与设备无关的软件的基本功能是执行对所有设备公共的IO功能,并且向用户层软件提供一个统一的接口。

  1. 设备驱动程序的统一接口
    • 与设备无关的软件要负责把符号化的设备名映射到适当的驱动程序中
  2. 缓冲
    • 双缓冲(内核空间)
    • 循环缓冲区
    • 缓冲的复制操作会很大程序上降低传输速率,因为这些步骤需要有序的发生。
  3. 错误报告
    • 编程错误:进程请求不可能的事情,提供了无效的缓冲区地址或者其他参数
    • 实际IO错误:比如试图写一个被破坏的磁盘块
  4. 分配与释放专用设备
    • 对于请求和释放专用设备有特殊机制
  5. 与设备无关的块大小

用户空间的IO软件

系统调用(包括IO系统调用)通常由库过程实现,所有这些库过程的集合是IO系统的组成部分。标准IO库包含许多涉及IO的过程,它们都是作为用户程序的一部分运行的。

另外一个类别假脱机系统,不是由库过程组成的。使用打印机需要创建一个守护进程和假脱机目录,通过保护特殊文件防止用户直接使用,可以解决某些进程不必要的长期空占打印机。

  • 层级
    • 硬件 -> 中断处理程序 -> 设备驱动程序 -> 与设备无关的软件 -> 用户进程
  • 请求:用户进程进行IO请求
  • 应答:
    • 执行IO操作 -> 当IO完成时候唤醒驱动程序 -> 设置设备寄存器,检查状态 -> 命名,保护,分块,缓冲,分配 -> 产生IO请求,对IO进行格式化,假脱机