理解输入输出

673 阅读18分钟

什么是 I/O

程序的执行,避免不了数据交换,就程序本身如何执行的这一信息,也涉及数据在内存与磁盘的交换。那么I/O,就是将数据从一个地方,传输到另一个地方。对于计算机而言,就需要管理所有的I/O设备,其目的在于:

  • I/O端如何连接
  • 如何得知I/O各端的状态
  • 如何在I/O各端传输数据

I/O 硬件原理

对于不同职业角色,要对I/O硬件的理解是不同的,对于咱们程序员而言,更注重I/O硬件提供给软件的接口。

I/O设备

I/O设备大致可以分为两类:块设备字符设备。虽然如时钟等不能按此分类,但此分类也具有一定的一般性。

块设备 把信息存储在固定大小的块中,每个块有自己的地址,大小通常在 2^9 ~ 2^16 bit,所有传输以一个或者多个的连续块为单位。特征是,每个块都能独立于其他块读写。如硬盘、蓝光光盘、UBS盘等是常见的块设备。

字符设备 以字符为单位发送或接收一个字符流,不考虑任何块结构,不可寻址,无寻道操作。如打印机、网络接口、鼠标等。

设备控制器

I/O设备一般由机械部件和电子部件两部分组成,通常分开处理。电子部件称作设备控制器适配器,常以主板上的芯片形式出现,或以插入PCI扩展槽中的印刷电路板的形式出现。

设备控制器与设备之间有很低层次的接口,工作时,从设备传出一串bit流,以一个前导符开始,最后是一个校验和。因此,设备控制器的任务是把串行的bit流数据转换为字节块,并进行必要的错误校正工作。

那么,设备控制器如何与CPU进行工作呢?

与CPU通信

在设备控制器中,有几个寄存器用来与CPU进行通信。操作系统可以通过写入这些寄存器,命令设备进行一定的操作,能可以了解到设备的状态。许多设备,还有一个可以让操作系统读写的数据缓冲区,那么CPU如何与设备控制器以及读写缓冲区进行通信呢?

与CPU通信
通信1:I/O端口 给每个设备控制器的控制寄存器,分配一个I/O端口号(8或16位的整数)。所有的I/O端口形成了I/O端口空间,收到保护并使普通的用户程序不能进行访问。如用特殊的I/O指令,就可以一些通信,如:

IN REG,PORT
读取PORT的内容到CPU的REG中

OUT PORT,REG
将CPU的REG内容写入到PORT中

如果需要特殊的I/O指令读写设备控制器的寄存器,就需要汇编代码,调用增加了控制I/O的开销。

通信2:内存映射I/O 将每个设备控制器的控制寄存器映射到内存空间中,每个控制寄存器会被分配唯一的一个内存地址,其他的内存不会使用到这些被分配的地址。当有I/O请求时,通过比较地址,如果地址处于映射范围,那么对应的设备控制器将会进行响应。内存映射I/O的好处在于:

  • 控制寄存器成为了内存总的变量,程序语言可以像其他变量一样对控制寄存器寻址,因此可以进行特殊的I/O操作。操作系统不需要特殊的保护机制观测用户程序执行I/O操作,只需保证避免把内存映射的地址,放入任何用户的虚拟地址空间中。
  • 控制寄存器可以映射到不同的页面上,意味着可以让不同的设备驱动程序放在不同的地址空间中,那么操作系统只要控制页表就可以让用户程序控制特定的设备。不仅减少了内核的大小,还防止驱动程序之间的相互干扰。
  • 引用内存的指令操作,也可以用来引用控制寄存器。如测试一个内存字是否为0的指令,也可以用来测试一个控制寄存器是否为0.

其要考虑的问题在于:

  • 能够选择性对每个页面禁用高速缓存。如果操作系统一直访问高速缓存中设备的状态,而不去访问设备,就有可能设备状态已更改但是操作系统不得而知。这增加了操作系统管理的复杂性。
  • 如果内存映射的机器上,有单独的内存总线,那么I/O设备没有办法查看内存地址,因为内存地址旁路到单独的内存总线上,所以没办法响应。为了解决这个问题,无论是将全部内存引用发送到内存中,响应失败时尝试其他总线;还是放置探测设备,放过潜在地指向缩关注的I/O设备的地址;或者对地址进行过滤以判断哪些内存地址不是真正的内存地址。都需要额外的复杂性,并使的I/O处理速度更慢。

总线

总线,是用来连接各个设备或连接点以支持传输数据的通路。总线的英文名叫“Bus”,故名思义,各个设备是公交站,站与站之间的传输依靠Bus才可达成。

两个设备或者连接点之间,通过导线发送数据位来项目通信,能支持串行与并行通信。以二进制 01000011为例子,简单的通信模型为: 总线并行通信 图片来源

并行通信,在一个Clock周期内,每个位只能是0或1,通过检查不同位,最终得到01000011。

图片串行通信 图片来源

串行通信,在Clock周期内,逐个发送每一位的信号,总重合并成01000011。

总线内容简单了解即可,以说明再在计算机中,设备与设备,或连接点与连接点的传输,均需要总线的参与。

DMA(Direct Memory Access,直接存储器读取)

无论内存与设备间交换数据,或是设备与设备间减缓数据,如果总需要CPU参与,对I/O控制器进行请求,将浪费CPU时间。DMA用以解决此问题,让数据传输时不需要CPU的参与。 无论CPU如何与设备交换数据,都需要与I/O控制器进行请求,但这样浪费CPU时间。

DMA 传输过程

DMA可以独立于CPU访问总线,工作过程如下:

  1. CPU通过设置DMA的控制寄存器进行编程,DMA就知道要将什么数据传输到什么地方。
  2. DMA通过总线,对磁盘控制器发送请求。
  3. 磁盘控制器将数据读取到内部的缓冲区,校验无误后,写到总线的地址线上的内存地址。
  4. 磁盘控制器写入完成后,在总线上发出一个应答信号给DMA。
  5. DMA检查计数,是否还有要读的数据,如果有则重复3~5,直到计数达到0。
  6. 所有传输完成后,DMA将中断CPU,以让CPU知道传输完成。

DMA能以字模式块模式进行数据传输。

字模式 在这种模式下,DMA每次请求并传输一个字,在此过程中,如果CPU也想使用总线,必须等待。这一机制称为周期窃取,因为设备控制器偷偷偷走一个临时的总线周期,轻微地延迟CPU。

块模式 在这个模式下,DMA通知设备获得总线,发送一连串的传送,然后释放总线。这一机制称为突发模式,比周期窃取效率更高,获得总线占用了时间,并且一次传入了多个字。缺点为,如果突发时间长,可能让CPU和其他设备阻塞时间过长。

除了上面的两种模式,还有一种称为飞越模式的传输方式,其工作机制为:

  1. DMA通知设备控制器直接将传输数据到主存。
  2. 如果DMA支持,则让设备控制器将字发送给DMA。
  3. 在DMA收到这个字后,发起一个总线请求,询问这个字要发往何处。
  4. 这种方案每传送一个字需要一个额外的总线周期,但是更加灵活,可以处理设备到设备,甚至内存到内存的复制。

I/O 软件原理

从软件层面看I/O,需要达成一定的软件目标,以使能有效率地工作:

  1. 设备独立性:程序可以访问任意I/O设备,无需事先指定设备。
  2. 统一命名:设备的名字应该是一个简单的字符串或者一个整数,不应依赖于设备。
  3. 错误处理:错误应尽可能地在接近硬件的层面得到处理,尽可能地对高层软件屏蔽此信息。
  4. 传输:同步传输,即阻塞;异步传输,即中断驱动。
  5. 缓冲:消除读写双方速率差别的影响。

控制I/O

控制I/O过程,可以采用三种不同的方式来实现。

程序控制I/O

最简单的形式就是,让CPU负责全部工作。假设一个用户进程需要打印机字符串,那么过程如下图: 程序控制I_O

  1. 首先,用户进程在用户空间的一个缓冲区中准备字符串,然后通过系统调用来获得打印机。直到获得打印机后,再发出系统调用,将字符串复制到内核空间中。
  2. 操作系统查看打印机的状态,如果打印机准备好接受下一字符,就复制字符发送到打印机寄存器中以打印。此时,打印机状态为非就绪。
  3. 操作系统复制完字符后,查看是否还有字符要打印,查看打印机是否就绪。打印机再次准备好后,重置状态寄存器状态为就绪。
  4. 重复2-3直到整个打印完成,最后,控制返回到用户进程。

在这种方式中,CPU需要不断地查看I/O设备的状态,虽然过程简单,但是忙等待无意是低效的,在全部I/O完成之前要占用全部的CPU。CPU耗费的时间更多地取决于设备的处理能力。

中断驱动I/O

在程序控制I/O中,CPU耗费了时间在无价值的忙等在中。可以使用中断,以释放CPU等待设备的过程去处理其他事情。那么,同样是打印,过程变成如下

中断驱动I_O

  1. 字符串被复制到在内核空间中。
  2. 在打印机准备好接收字符后,产生一个中断,此中断将阻塞当前进程。CPU将去处理别的事情。
  3. 打印机接受到字符后,进行处理,处理完毕后,产生中断。如果还有字符要打印,中断服务过程将重复此过程直到字符打印完成。
  4. 全部打印完毕后,收到中断的中断服务过程采取某个操作解除阻塞。

DMA的I/O

中断驱动I/O的问题在于,中断发生在每一字符上,而CPU处理中断也是耗费时间的。那么使用DMA来处理I/O过程能解决这一问题。

在前面的DMA小节也有看到,DMA直到缓冲区的内容处理完后,才发送中断,以让CPU处理。DMA让每个字符发生一次中断,改变为每个缓冲区发生一次中断。虽然DMA比CPU处理更慢,但除非CPU没有其他事情做,否则使用DMA处理I/O过程比前面两种更好。

中断

中断是异步机制,是帮助并发能够执行并有效执行的基础保证。各种异步机制,都可以看成是对中断的模仿。发生了中断,就说明出现了一些情况,需要有CPU来处理,而专门处理这种事情的服务,也叫中断服务程序。

取一个简单的例子来说,可以把你的手机看作中断处理器,你就是CPU。此时打来了一个电话,告诉你“你的快递到了”,接着你去取了快递。这就是发生了一个中断,并且需要CPU处理。当然,也可能打来的电话告诉你“你中奖啦!”,你直接把当作诈骗置之不理。这也是中断,是异常,同样需要CPU处理。

中断控制 粗略地看,中断的工作机制为:

  • 当设备完成工作时,通过在分配给它的一条总线信号线上发送信号,产生中断。
  • 主板上的中断控制器芯片检测到了中断,此时,它将决定接下来要做什么。
  • 如果没有其他的中断要处理,那么中断控制器立刻处理此中断。
  • 如果有其他中断在处理中,或者有更高优先级的中断也发出了中断请求,那么中断控制器将通过总线,向CPU置起中断信号。
  • CPU立即停止当前在做的工作,处理中断,以决定继续执行哪个工作。

为了能让CPU能处理中断,中断控制器在地址线上放置一个数字,表明哪个设备需要关注。地址线上的数字指向称为中断向量的表格索引,用来读取一个新的程序计数器。通过中断向量就能找到中断处理函数的口。

在中断服务开始后,立即将一个确定的指写到中断控制器的某个I/O端口来对中断进行应答,以告知中断控制器,可以自由地发出另一个中断。通过让CPU延迟应答直到准备好处理下一个中断,就能避免可能的几乎同时发生的中断相牵制的竞争状态。

精确中断和不精确中断

中断带来效率的同时,也带来了问题,如,如何确定发生中断时,程序指令执行到哪了。如果一条指令完全执行之后,等恢复程序时,容易继续执行下去。但是当指令只执行了一部分,那么在恢复程序时,就要考虑发生了什么,以回到程序正确的状态。如前者,将机器留在一个明确状态的中断称为精确中断,后者则为不精确中断。

满足精确中断的特征为:

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

精确中断和不精确中断

为精确中断的系统恢复现场是相对容易的,因为程序处于一个确定的状态。而对于不精确中断,可能只执行了指令的一部分,中断可能发生在在指令执行过程中的任意部分,为了推断出程序的确切状态,就需要大量复杂的代码进行应对。

往往更先进的系统支持不精确中断,但是却要耗费更多的CPU时间处理不精确中断,因此难以判断精确中断更好还是不精确中断更好。

软中断和硬中断

概念上,由硬件产生的中断称为硬中断,执行中断指令产生的中断为软中断。如何理解?

对于中断的处理,需要尽可能地尽快处理,以让中断处理程序尽快地运行。否则,可能导致中断丢失。还是之前的例子,如果快递的电话一直占线,那么可能的工作电话就打不进来了。那么快速的回应快递电话,然后等有空时再去取快递,大大降低了其他电话打不进来的可能性。

对应地,中断就可以分成上半部下半部。上半部用来快速处理中断,也就是硬中断,由中断控制器提供,此处于禁用中断模式下运行,带中断服务程序快速地处理并响应后,中断控制器才放开禁用限制;下半部则用来延迟处理上半部未完成的工作,也就是软中断,通常以内核线程的方式运行,这意味着,CPU知道这件事情了,会找合适的时间进行处理。

I/O软件设计

有了I/O硬件原理、I/O软件原理做铺垫,就可以观测I/O软件层次如何构成。 I_O软件系统的层次

中断处理程序 中断是难以处理并不可避免的,因此应深深隐藏在操作系统内部,尽可能不与其他部分发生联系。当发生中断时,由中断处理程序对对中断进行处理。在I/O上,将对应的进程阻塞,直到中断完全处理完毕。

设备驱动程序 由设备的制造商编写的以能和计算机进行连接的代码为设备驱动程序。设备驱动程序就要完成以下目标:

  • 实现操作系统规定的必须支持的接口,以能进行语义相符的操作。
  • 能正确访问设备寄存器,以提供正确的状态。
  • 对设备发出命令后,能阻塞自身,直到设备准备好后,中断到来而解除阻塞
  • 可重入,设备驱动程序在第一次调用完成之前,能被第二次调用。
  • 设备驱动程序常会作为内核空间的一部分,需要这样的机制将其装载入内核空间。

设备无关的操作系统软件 操作系统需要有驱动程序,能与设备驱动程序进行交互,能响应上层程序对I/O的请求:

  • 提供统一的设备驱动程序的接口,可使新设备能接入而不必修改操作系统。
  • 有缓存机制,减少读写时的中断频次,提升效率。
  • 有错误报告,如果操作系统不能够解决的I/O错误,能向上层传递给调用者。
  • 能分配和释放专用设备。
  • 有与设备无关的块大小,虽然设备间的扇区大小不同,但在此抽象中块大小应是一致。

用户空间的I/O软件 由用户程序编写的程序,主要目的是能正确地产生I/O请求,对I/O进行格式化。

总结

I/O,就是将数据从一个地方,传输到另一点地方。在此过程中,需要软硬件的配合才能达成目的,并避免许多问题:

  • 首先,设备之间或连接点之间的数据传输,需要总线的参与,通过数据信号进行传输,此为传输的基础。
  • 然后,设备驱动程序编写了与实际设备息息相关的具体读写操作,屏蔽了设备有关的细节。提供了特定的寄存器,用来告知设备的状态,和交互数据。
  • 而I/O的过程CPU不应过多地参与,效率更高的CPU需要将更多地并发。那么,就需要有中断,来使得I/O能异步完成,并在完成后能通知到CPU,使地调用I/O的进程能恢复继而继续执行。
  • 紧接着,控制I/O过程就由DMA控制,DMA在每次缓冲区的数据传输完毕后,才会产生中断,告知CPU,CPU再决定是否还有后续的I/O操作。
  • 那么,还要有驱动程序,为用户程序分配相应对的I/O设备,为设备驱动程序提供统一的接口,使得设备能够灵活地添加、处理。
  • 最后,用户程序,就能按照规则完成I/O,而不必考虑种种繁琐的实际I/O细节。

文章大多数内容来自《现代操作系统 第四版》第五章,错误之处,望请指出。

参考

谁能给我解释一下什么叫计算机【总线】?

linux 中断机制

Linux-怎么理解软中断

(转)计算机中断体系二:中断处理