• 【知识强化】第五章 输入/输出(I/O)管理 5.1 I/O管理概述


    这门课的最后一个章节——设备管理。

    操作系统它作为系统资源的管理者,既需要对上层的软件进行管理,也需要对下层的硬件进行管理。操作系统它需要对处理机还有存储器这些硬件进行管理,但是这些硬件其实是在计算机的主机内部的。那这个章节我们要探讨的所谓的设备管理其实指的是操作系统对计算机主机外部的那些硬件设备的一个管理。

    了解一下I/O设备的基本概念和分类。

    人机交互类外部设备传输速度会比较慢,数据的传输都是以字节或者几十字节为单位的。猫还有路由器这些就属于网络通信设备。网络通信设备的传输速度会比人机交互类外部设备更快一些,又会比存储类的外部设备要更慢一些。

    因为我们每敲击一次键盘其实只是往计算机当中输入了一个字符,所以这种设备和计算机进行数据交换的速度是很慢的。高速设备,像平时咱们使用的移动硬盘。那其实高中低速并没有一个很明确的这种界限划分,所以这个知识点大家也不需要特别地记忆,能有一个印象了解就可以了,基本不可能作为考点进行考查。

    移动硬盘、磁盘等等这些就属于块设备。那像字符设备的话就是类似于鼠标键盘这种,数据的传输基本单位是字符或者字节。那中断的概念咱们在第一章也有比较详细的介绍,在下个小节中我们还会继续介绍什么叫中断驱动方式。

    那像前面的这两种分类方式,其实并不会有很明确的那种划分的界限,所以这两种分类方式其实一般来说也不太可能进行考查。因此大家需要重点关注的是第三种分类方式,也就是按信息交换的单位分类,分为块设备和字符设备。大家需要注意它们之间的区别。那这两类设备最本质的区别是,这种设备与计算机进行数据交换的时候,这种数据交换的基本单位,一个是块,一个是字符。那除此之外,大家还需要结合上一章的内容理解块设备是可寻址的,而字符设备是不可寻址的,这样的两个特性。那字符设备通常会采用中断驱动的这种I/O控制方式,那这也是我们下个小节会详细介绍的内容。

    学习I/O控制器相关的内容。那学习了这个小节的内容之后有助于我们理解之后的小节会讲解的I/O控制方式。

    那在操作系统这门课当中重点需要掌握的是电子部件相关的一些知识。其实当我们的这些I/O设备连上电脑之后,我们的CPU是没办法直接控制这些I/O设备的机械部件的,它必须通过电子部件来间接地控制这些机械部件。

    那我们最熟悉、最简单的命令当然就是读或者写的命令。那除了命令本身之外,CPU还会告诉I/O控制器执行这个命令相关的一些参数。比如说要读多少个字节,要写多少个字节。

    因此I/O控制器当中需要设置一个叫做控制寄存器的东西,就是用来存放CPU发出的命令和参数的。那之后I/O控制器就可以根据这个寄存器当中存放的这些数据来确定自己要执行的到底是什么样一个操作了。那除了接收和识别CPU发出的命令之外,I/O控制器还需要能够向CPU报告设备的状态。比如说一个设备此时到底是忙碌的还是空闲的,还是说这个设备此时处于故障的状态。那CPU作为系统资源的管理者,当然也需要知道各个设备的相应的状态。

    所以I/O控制器当中也会设置一个叫做状态寄存器的东西。那CPU可以读取这个寄存器当中的内容来判断一个I/O设备此时的状态。当然如果还有其他更复杂的状态的话,那我们可以用更多的二进制位来表示。

    因为I/O控制器它是CPU和I/O设备机械部件之间的中介。所以它作为这个中介,当然也需要负责作为这个数据交换的中间的一个使者。

    这个I/O控制器当中会设置各种各样的寄存器,并且每一种寄存器可能会有多个。那为了识别这些寄存器,我们也需要像给内存编址一样给各个寄存器编上一个相应的地址。那CPU在往这些寄存器当中读或者写数据的时候,就是通过这些寄存器对应的地址来进行操作的。

    了解了I/O控制器的功能,还有各个功能大致需要怎么实现之后,我们来看一下I/O控制器应该由哪些部分组成。I/O控制器它其实是作为CPU和I/O设备的机械部件之间的一个中介的关系。而这个中介在连接CPU和I/O设备的时候,必然是需要做一些中间的处理的。那这些处理主要就是在I/O逻辑这个部分来完成的。

    所以I/O逻辑,当然像咱们刚才提到的,地址识别或者说地址译码也是这个I/O逻辑需要进行的。那在接收和识别了CPU的命令之后,它还需要把它翻译成具体的设备能够明白的一些命令,然后通过这个控制器与设备之间的接口发送给具体的设备,让设备执行相应的操作。那这个地方大家会发现,一个I/O控制器它可能会有多个控制器与设备之间的接口,也就是说一个I/O控制器有可能会负责控制多个具体的I/O设备。那为了区别CPU此时要操作的到底是哪一个设备,同样需要给这些设备进行一个编号,或者说给各个设备接口一个地址,那CPU在发出I/O命令的时候也需要指明自己需要操纵的是哪个设备。

    CPU首先会通过一个叫做控制线的线路,向I/O控制器发出一个具体的I/O指令。同时CPU还会在地址线这样的一个线路上说明自己要操纵的是哪一个设备。那如果说此时是要输出一个数据的话,那CPU会通过数据总线把自己要输出的数据放到I/O控制器的数据寄存器当中。那之后I/O逻辑就可以从数据寄存器当中取得CPU想要输出的数据。那类似的,CPU此时发出的这个I/O指令可能会有一些相应的参数,那这些参数它会放到控制寄存器当中。那I/O逻辑就可以从控制寄存器当中读出相应的参数了。另外,为了实现对各个设备的管理,CPU还会从这个状态寄存器当中读出各个设备的一个状态,比如说忙碌、空闲还是故障等等。那I/O逻辑会往状态寄存器当中写入相应的数据来告诉CPU各个设备的状态到底是什么样的。

     那这就是CPU与控制器的接口所需要负责完成的一些事情。这个接口主要就是用于完成CPU和控制器之间的通信。

    那类似的,控制器与设备的接口其实就是用于完成控制器和设备之间的一个通信。那比如说此时是要输出一个数据的话,首先就是由CPU通过数据总线把数据写入到数据寄存器当中,然后I/O逻辑取出数据寄存器当中的内容,然后通过控制器与设备的接口、数据通路把这些数据输出到外部设备上。那类似的,如果要输入一个数据的话,这些数据可以通过控制器与设备的接口输入,然后I/O逻辑会把这些数据放到数据寄存器当中。之后CPU又从数据寄存器当中取走数据,那这就完成了一个数据输入的过程。

     那除了这样一个传送数据的通路之外,设备还需要及时地向I/O控制器反馈自己的状态。比如说这个设备到底是忙碌还是空闲,那同样的设备通过这个接口向I/O控制器报告此时自己的状态,然后I/O控制器的I/O逻辑又会把这个设备的状态写入到它对应的状态寄存器当中。

    那最后这个接口中还会有一个用于实现设备控制的一个电路,那I/O逻辑会根据CPU发出的命令还有相应的参数然后对对应的设备发出一些控制命令,让这些设备执行具体的工作。那这就是I/O控制器的一个组成,分为这样的三个部分。

    这个地方有两个小细节比较值得注意。第二,由于这个控制器会对应多个设备,而各个设备需要输入输出的数据还有各个设备的状态这些肯定是不同的,所以如果这些设备同时工作的话,那么显然只设置一个数据寄存器、状态寄存器肯定是不能满足需求的。所以既然I/O控制器中会有多个寄存器,那为了识别各个寄存器,也需要给这些寄存器进行编址。

    我们来看一下内存映像I/O和寄存器独立编址的区别。不过在有的系统当中也有可能是各个设备控制器当中的寄存器用连续编号的方式。当然我们并不需要纠结这些细节,我们只需要知道如果采用的是寄存器独立编址这种方案的话,那么这些寄存器和内存的地址空间并不是统一的,它们是两个独立的体系。

    那采用这种方式有一个很明显的缺点,就是要设置一些专门的指令来实现对这些寄存器的一个存取操作。那像这个例子当中,各个控制器的这些寄存器它们的地址也都是相互独立的,那在这种情况下,我们不仅要指明我们要操作的寄存器的编号,同时还需要说明到底要操作的是哪一个控制器,是控制0还是控制器1。

    而如果我们采用的是内存映像I/O这种方式的话,那么我们就不需要设置专门的指令来支持对这些寄存器的操作。我们只需要用对内存单元的操作、相同的那些指令来操作这些寄存器就可以了。那这是这两种方式的一个区别还有它们的有优缺点。

    介绍了I/O控制器,也就是I/O设备的电子部件。那随着计算机的发展,也出现很多种对I/O设备的控制方式,这是咱们之后的那个小节会重点讲解的内容。那这个小节的内容考的频率并不是很高,但是大家也需要能有一个大致的印象。需要重点注意的是,两种寄存器编址方式的一个区别还有它们各自的优缺点,当然I/O控制器由3个部分组成,所以大家对于控制器的组成、主要功能这些也需要有个大体的印象。

    学习本章的一个重要的考点——I/O控制方式。但是随着计算机的发展,I/O控制器也是在不停地发展的。那相应的,I/O控制器对设备的控制方式也出现了不同的变化或者说进化。

    需要注意这样的几个问题。第一,在各种I/O控制方式当中,完成一次读写操作的流程分别是怎么样的。第二,我们需要注意的是CPU对这个I/O操作的一个干预的频率。第三,我们需要注意在不同的控制方式当中,进行一次I/O所要传送的数据的单位到底是多少。第四,我们还需要注意数据的流向。第五,我们需要注意的是各种控制方式的一个主要缺点和主要优点。那什么是干预频率,什么是传送单位,什么是数据流向,待会看具体的例子就可以理解了。

    首先要了解的是程序直接控制方式,这也是最早期的一种I/O控制方式。那如果要完成一个读操作的话,CPU首先会通过控制线向I/O控制器发出一个读指令,于是I/O控制器会根据CPU的要求启动相应的设备,并且把这个设备对应的状态设置为未就绪或者说忙碌的一个状态,那我们假设状态寄存器为1表示的是设备忙碌。那接下来这个设备就会开始准备计算机想要读入的数据,但是由于设备的速度要比CPU的速度慢很多,所以在设备还没有完成I/O之前,CPU会一直不断地轮询检查这个设备的状态。

    所以其实这个数据输入的过程,本来应该是从设备输入到内存的,但是这个过程中必须先经过CPU的寄存器,然后再由寄存器转存到内存当中。

    所以其实如果采用程序直接控制方式的话,那么我们需要掌握的一个重点的核心词叫做轮询。采用这种方式完成一次读写操作的流程,就像这个图表示的这样。其实读I/O模块的状态,是从I/O控制器的那个状态寄存器当中读出数据然后放到CPU的寄存器当中进行分析。因为I/O设备有可能会出现一些故障,那如果I/O设备出错的话,也会在I/O控制器的状态寄存器当中写入相应的那些错误代码。那CPU就可以根据这些代码来判断此时这个I/O设备是否已经产生错误了,所以这儿有可能会产生一个错误条件。那么我们知道我们定义的这些变量a,b,c,d其实它们是存放在存储器也就是内存当中的。所以其实这些数据从键盘读入之后,最终肯定是要被放到存储器也就是内存当中。因此,当CPU获得我们从键盘输入的数据之后,其实还没有结束,还需要把这些数据把它写入到相应的存储器的相应单元里。那原理相同,当我们使用printf这个输出数据的函数的时候,其实我们做的事情是要把内存当中存储的这些变量的数据拿出来然后最后经过CPU再输出到输出设备上。

    在使用程序直接控制方式这种方式的时候,CPU需要不断地轮询检查这个I/O操作是否已经完成,所以CPU干预的频率是很频繁的。不仅在I/O操作开始之前还有完成之后需要CPU的介入,在等待I/O完成的过程当中,这个CPU也需要不断地进行轮询检查。那这也是程序直接控制方式的一个最大的一个缺点。

    每一次读入或者写出的数据,数据量是一个字。

    所以每一个字的读和写都需要CPU的介入帮助,也就是说CPU需要花费大量的时间来辅助完成这个I/O的过程。

    优点的话就是实现简单,可以用软件的方式就可以实现。那由于它这个轮询的过程其实就是在执行一系列循环检查的指令,所以这种方式才叫程序直接控制方式。因为在CPU发出一个I/O指令之后,CPU并不能去做别的事情,它需要一直不断地循环检查这个I/O是否已经完成了。所以CPU会长期处于一个忙等的状态,导致CPU的利用率低。那相应的,当CPU在进行别的一些计算工作的时候,I/O设备肯定也是空闲的,所以I/O设备的利用率其实也是低的。

    为此人们提出了中断驱动方式。与程序直接控制方式相比,中断驱动方式主要是引入了中断机构,可以让CPU在发出了I/O指令之后,转头可以做别的事情,也就是可以切换到别的进程。那由于我们的I/O设备是速度很慢的,而CPU又是一种速度很快的一个硬件机构。当然也可以选择不恢复被阻塞的进程,让它继续在就绪队列里等待,然后先执行别的进程。在中断驱动方式当中,每次发生中断只能读入一个字的数据。所以如果要读入大量的数据的话,那显然会发生大量的中断,那这样的话就会导致系统的性能降低。

    那由于等待I/O完成的这个过程中,CPU可以切换到别的进程执行,所以在引入了中断之后才实现了CPU和I/O设备并行工作的这样的一个特点。

    每发出一个读或者写指令,只会读入或者写出一个字大小的数据。

    那中断驱动方式的优点呢其实就是解决了程序直接控制方式的最大的缺点。引入了中断技术之后,可以让CPU和I/O设备并行地工作,然后CPU不再需要不停地轮询来检查这个I/O是否完成,这样的话CPU的利用率还有I/O设备的利用率也得到了明显的提升。那这种方式也存在一个很明显的缺点,就是由于它每次只能传送一个字,所以当我们需要传送大量的数据的时候,那显然会发生很多次的中断,而每一次中断的处理又需要付出一定的时间代价,所以如果中断发生的太频繁,那么这个中断处理会消耗很多的CPU时间。另外呢,采用这种方式的时候,在读入数据或者写出数据的时候,都必须先经过CPU,但是通过之前的分析我们也知道,其实读入数据无非就是把I/O设备准备好的数据放到内存里,而写出数据无非就是把内存中的数据写出到I/O设备,所以能不能把中间就是必须经过CPU的中转这个步骤给砍掉呢?

    那为此,人们又提出了一种新的I/O控制方式——DMA方式。每次会读入或者写出一个块。

    另外呢,数据的流向不再需要经过CPU,而是可以在DMA控制器的控制下直接从设备放入到内存,或者直接从内存写出到设备。

    第三,CPU对于I/O操作的干预频率又进一步地降低。仅仅在传送一个或者多个数据块的开始和结束的时候,才需要CPU进行干预。

    DMA控制器其实也是一种I/O控制器,只不过它和咱们上一小节介绍的I/O控制器有那么一些小小的区别。

    不过DMA控制器依然是由3个部分组成。第一个部分是主机或者说CPU和控制器的接口,第二个部分是I/O控制逻辑,第三个部分是块设备和控制器的接口。那这和I/O控制器的3个部分都是一一对应的。那为了实现控制器和CPU之间的通信,它会在这个地方设置一系列的寄存器。然后CPU可以通过系统总线来读或者写其中的某一些寄存器当中的内容,用这种方式达到控制I/O设备的一个目的。

    DR相当于一个中转站,这个和咱们之前介绍的数据寄存器没有太大的区别。MAR它是用于存放内存地址的。那和之前咱们介绍的I/O控制器一样,这些寄存器也有可能会有多个,在这个地方并没有列全。那这些寄存器是最主要的主机和控制器之间的接口。

    而在控制器和块设备之间也有一个相应的接口,通过这个接口可以实现控制器对于这些块设备的一个通信、控制的一个过程。

    那除此之外,系统总线还会把DMA控制器和内存连接在一起。所以DMA控制器和内存之间可以直接进行数据的读写,不再需要经过CPU。比如说CPU可以在刚开始指明这次要读入的数据是存放在磁盘的什么位置,那这些读入的数据应该存放在内存的什么位置,这些信息是存放在MAR里的。并且还要说明此次要读入的数据的数据量到底是多少,那这些数据量又是存放在DC这个寄存器当中。

    那接下来DMA控制器就会根据CPU提供的这一系列的参数从磁盘的相应位置读入数据,然后写到内存里。而这个过程就不再需要CPU的干预,只有DMA控制器完成了整个CPU指定的这一系列的任务之后,它才会向CPU发出一个中断信号,然后CPU再介入进行后续的处理。那这个地方需要注意的是,DMA控制器并不是每次直接读入一整块的数据,然后直接把一整块放到内存当中。其实DMA控制器在读入数据的过程当中,也是一个字一个字读入的。然后每次读入了一个字都是先会存放在DR也就是数据寄存器当中,再从DR写入到内存当中。用这样一个字一个字的方式,最终就可以完成一整块的数据的读入工作。

    在采用这种方式之后,CPU的干预频率就进一步地降低了。在开始之前CPU需要发出相应的I/O指令并且指明那些相应的参数。然后在结束之后CPU又需要处理中断然后进行后续的一系列处理。

    而数据的传送单位也从一个字变成了一个块。CPU每发出一个读指令或者写指令之后,DMA控制器就可以完成对一个块或者多个块的读和写的操作。但是需要注意的是,这个地指的多个块只能是读写那些连续的多个块,并且这些块在读入内存之后也必须是连续存放的。也就是说,如果我们是想要读入多个离散的块,或者这些读入的块需要离散地存放在内存的不同位置的话,那么采用DMA方式同样是需要CPU发出多条这个I/O指令。

    那在采用了DMA方式之后,数据的流向就不再需要经过CPU,还可以直接从I/O设备读入,然后在DMA控制器的控制下直接把数据放入到内存。在输出的时候就刚好相反,同样是不需要经过CPU的。

    所以这种方式的优点呢就是进一步提升了这个数据传输的效率。数据传输以“块”为单位,然后CPU介入的频率可以进一步地降低,这样的话CPU就可以有更多的时间去进行别的处理。另外,数据传输的过程也不需要再经过CPU,所以数据传输的效率也得到了进一步的提升。那所有的这些其实带来的结果都是,可以让CPU从这些繁杂的I/O工作当中抽离出来,让CPU有更多的时间去处理别的那些计算任务。所以采用这种方式之后,I/O设备和CPU的并行性得到了进一步的提升,资源利用率也得到了进一步的提升。

    如果我们要读取离散的数据块,或者读入的数据块要离散地存放在不同的内存区域当中的时候,就需要发出多条I/O指令。因此这是DMA方式还需要继续改进的一个特点。

    那为了解决这个问题,人们又提出了通道控制方式。通道可以识别并且执行一系列的通道指令,就类似于咱们CPU识别的那些指令一样。那为什么叫“弱鸡版的CPU”这个咱们一会儿再解释。我们首先来看一下通道的工作原理。CPU、内存、通道通过系统总线连接到一起。首先CPU会向通道这个硬件发出I/O指令,并且指明此次要执行的通道程序或者说通道指令的序列它是存放在内存的什么位置的。同时CPU还需要指明此次要执行操作的设备到底是哪一个。那在把这些信息告诉了通道之后,CPU就可以切换到其他进程执行了,可以抽离出去开始干别的事情。

    那么之后通道会根据CPU的指示去找到此次要执行的通道程序存放在内存当中的什么位置。那这个通道程序其实大家可以把它理解成是一种任务清单,其实这个任务清单就是一系列通道指令的集合。本质上它和我们熟悉的,普通的那些程序其实都是一样的。那在这个任务清单的通道指令当中,也会向通道指明此次要读入和写出的数据到底是多少,读写的数据应该放在内存当中的什么位置,它是放在外存中的什么位置等等这一系列的信息。这些都是通道在执行这个程序的过程当中就可以知道的事情。所以如果采用这种方式的话,就相当于CPU只是告诉通道你现在去执行这样的一个任务,那这个任务的清单我已经放在内存里了。但具体这个任务需要做什么,并不是由CPU直接告诉通道的,而是由通道直接去读取内存当中的这个程序,然后一步一步执行。

    那当这个通道执行完了这一系列的任务之后它就会向CPU发出一个中断信号。CPU接收到中断信号之后对中断进行处理,然后再继续执行接下来的那一系列的程序。所以这就是通道控制方式当中完成一次I/O所需要经历的一系列步骤。

    那为什么说通道是一种“弱鸡版”的CPU呢?因为通道它可以识别一系列的通道指令,但是它所能识别的这些指令与CPU能识别的那些指令相比,它的指令是很单一的,并且通道也并没有自己的内存,它需要和CPU共享主机的内存。所以由于通道这个硬件只能识别一些很简单的、很单一的通道指令,因此我们可以把它理解为是一个“弱鸡版”的CPU。

    而引入了通道之后,CPU的干预频率就进一步地降低了。CPU可以一次扔给通道一堆事情,那这些事情会写在通道程序里。所以通道可以根据通道程序的指示,一步一步完成I/O操作。然后当它完成了一系列的数据块的读写之后,才需要对CPU发出中断信号。因此CPU的干预频率是极低的。

    那DMA控制方式当中每一次读写是读写一个数据块或者多个连续的数据块。但是在通道控制方式当中,每一次读写可以完成对一组数据块的读写操作。

    那与DMA控制方式类似,采用了通道控制方式之后,可以在通道的控制下,让数据直接从I/O设备读入内存,或者直接把内存中的数据输出到I/O设备当中。

    那通道方式的主要缺点呢就是

    四种我们需要掌握的I/O控制方式,分别是程序直接控制方式、中断驱动方式、DMA方式和通道控制方式。首先我们需要理清楚在每一种控制方式下完成一次读写操作的大致过程、大致流程是怎么样的,这个经常在选择题当中进行考查。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硬件做完这次I/O操作,发出I/O应答的时候,又会由这些层次从下往上依次进行处理,最后返回给用户。那接下来我们按从上至下的顺序依次分析一下各个层次所需要实现的功能。

    首先来看用户层需要实现什么功能。用户层作为最接近用户的一个层次,那它肯定是需要向用户提供一些简单易用的交互的接口。那一般来说,用户层软件会向用户提供一些与I/O操作相关的库函数,让用户调用这些库函数来对设备进行操作。比如说咱们很熟悉的C语言里的Hello World,printf也就是在显示屏这个I/O设备上打印输出Hello World这样一句简单的代码,其实这个printf库函数就是由用户层软件提供的。那既然需要使用I/O设备进行输出操作,所以用户层软件肯定需要请求操作系统提供服务。因为只有操作系统才有对硬件操作的权利。

    因此用户层软件会使用设备独立性软件这一层向上提供的系统调用接口来请求操作系统内核的服务。

    比如说printf("hello world!");这样一句代码,在用户层软件处理完了之后,会把它翻译成等价的write系统调用,当然在进行调用的时候它也会填入相应的参数,比如说要打印输出的内容——"hello,world!"这个字符串。所以其实设备独立性软件向上层提供了系统调用的接口。那设备独立性软件又是用来处理这个系统调用的一个层次,因此在有的题目当中它也会把这一层称为系统调用处理层,这一点大家稍微要注意一下。

    但是由于系统调用太低级了,还需要把它进行进一步的封装才更方便用户使用。所以Windows操作系统又会在用户层进行进一步的封装,然后向用户提供一些库函数接口。Windows API就是用户层软件向用户提供的库函数接口。那这是用户层软件需要做的事情。

    设备独立性软件要实现一些什么功能呢?

    在很多操作系统当中,设备会被看作是一种特殊的文件。相应的,不同的用户对设备这种特殊的文件的访问权限肯定也是不一样的。因此,操作系统需要提供设备保护的功能。

    那这个地方大家能有个印象就可以了,不需要了解差错处理的细节。因为差错的类型实在是太多了,所以对差错的处理基本上不太可能进行考查。

    因为很多设备它其实是一种临界资源,不可以同时分配给多个进程使用,所以操作系统当然需要对设备这种资源进行分配与回收的管理。那这个咱们在之后也还会细聊。

    关于缓冲区管理应该怎么实现这个很容易在选择题当中进行考查,不过这儿先不展开细聊,咱们在之后的小节中还会专门讲解。

    所谓逻辑设备名就是用户在请求使用一个设备的时候,所提供的名字。也就是用户所看到的设备名。

    那操作系统对这些设备进行管理,在背后还会有一个叫做物理设备名的东西。所以当我们选择某一个逻辑设备的时候,操作系统当然需要知道这个逻辑设备具体对应的到底是哪一个物理设备。

    那一般来说这个映射关系,是通过一个叫做“逻辑设备表”的东西来实现的。英文缩写是LUT。并且在这个逻辑设备表中,还会记录每一个逻辑设备对应的设备驱动程序的入口地址。

    就像这个样子。这儿的逻辑设备名竟然还带有一个类似于文件路径的东西。很多操作系统都会把设备当作是一种特殊的文件,所以这个文件当然也会有个存储的路径。

    那每一个表项记录了逻辑设备名到物理设备名的一个映射关系。并且还记录了这个设备它所对应的驱动程序的入口地址是在什么地方。

    但是由于各个用户在使用设备的时候使用的都是逻辑设备名,而操作系统又是根据逻辑设备名来查找LUT的表项的。所以如果不同的用户使用相同的逻辑设备名的话,那么就有可能会导致这个逻辑设备到物理设备映射紊乱的问题,所以其实整个系统只设置一张逻辑设备表这种方式只适合于单用户的操作系统。

    那采用这种方式的话,不同用户所使用的逻辑设备名可以是重复的,并且相同的逻辑设备名可以被映射到不同的物理设备上去。那其实这两种方式有没有发现,它就有点类似于我们在文件管理当中学到的单级目录和两级目录这样的区别呢?在文件系统中,如果采用的是单级目录结构的话,那么不同用户的文件名是不允许相同的。但是在两级目录的结构下,不同用户的文件名可以是相同的。那这里为每个用户设置了一张逻辑设备表,其实在本质上就有点类似于设置了两级目录这样的结构。所以其实这两种方案,和我们之前学过的知识点是有一些内在联系的。

    不同型号的这些设备,它们内部的那些电子部件,有可能是完全不一样的。所以如果操作系统要通过这些设备的控制器来控制这些设备的运行的话,那么操作系统肯定是需要了解这些设备内部的硬件细节的。不过这些I/O设备多种多样,所以操作系统不可能了解所有的这些设备的内部细节。因此这些设备在出厂的时候,一般来说厂家会提供一个与设备对应的所谓的驱动程序。然后当要控制某一个设备的时候,CPU其实只需要执行这个设备相对应的驱动程序就可以完成对这个设备控制器的控制了。比如说想设置控制器里面的寄存器,或者检查每个设备的状态寄存器这些工作就是可以通过执行驱动程序来完成。

    那这个自动安装的驱动程序其实就是由厂家提供的,为了让操作系统实现对这个新的设备硬件进行具体控制的一个程序。所以其实设备独立性软件不可以直接操纵硬件,它必须调用厂家提供的设备驱动程序,由这个设备驱动程序来完成对硬件的具体控制。比如说设置设备里的各种寄存器等等这些操作。

    那像各个类型的设备驱动程序一般来说在系统中会以一个独立进程的方式运行存在。所以我们再回到刚才的这个逻辑设备表,为什么不同的设备需要对应不同的驱动程序呢?就是因为各种设备内部的硬件特性是不一样的,因此必须执行与它对应的特定的驱动程序才可以正常地完成对这个设备硬件的控制。那这是设备驱动程序这一层需要完成的一个事情。

    当我们的硬件设备完成了I/O操作之后,它会发出一个中断信号作为I/O应答。那系统会根据这个中断信号的类型来找到与这个中断信号对应的中断处理程序,然后执行这个程序进行中断处理。那中断处理程序对中断的处理流程是这样子的:首先中断处理程序会从I/O控制器或者说设备控制器当中读出设备的状态,来判断这次的I/O是不是正常地结束。如果此次是正常地结束,那接下来中断处理程序会从设备控制器的数据寄存器当中读出一个字的数据,并且经由CPU然后放到内存缓存区当中,这就完成了一个字的读入。而如果此次I/O是非正常结束的,也就是中间发生了什么意外,比如说像硬件故障啊之类的。那系统会根据异常的原因做出相应的处理。所以这就是中断处理程序所需要做的一件事情。那当中断处理程序把这一次要输入的数据放入到内存之后,接下来又会交由设备驱动程序对这些数据进行进一步的处理。等设备驱动程序处理完了它又会交由再上一层的设备独立性软件进行再进一步的处理,最后一层一层往上,然后一直返回给用户。所以如果要输入一个数据的话,那么对于这个数据的处理应该是从下往上依次层层处理的。

    除了设备驱动程序会直接和硬件打交道之外,其实中断处理程序它也需要直接和硬件打交道。但是再往上的设备独立性软件和用户层软件,就不会直接和硬件打交道了。

    我们对I/O软件的层次结构进行了从上至下的分析,那要完成一个I/O操作,除了软件的支持之外,肯定也离不开硬件。硬件才是执行具体的I/O操作的部件。它由机械部件和电子部件组成,那这是咱们之前的小节谈到的内容。那如果一个用户发出I/O请求的话,这个I/O请求会由上至下依次由这些各个层次进行处理,最后扔给这个I/O硬件实行具体的I/O操作。而如果硬件完成了I/O操作的话,它又会发出一个中断信号,接下来会由这些软件层次由下至上地层层处理,然后最后返回给用户。那这个小节的内容最常考的其实是各个层次之间的顺序,比如说它经常会问一个I/O请求的处理次序到底是怎么样的。或者一发生I/O应答的时候,每一层软件的处理顺序依次是怎么样的。所以大家需要理解并且记住这些各个层次的从上至下的这种顺序。另外呢,也有可能考查各个层次要完成一些什么功能。那大家需要抓住一个最重要的特点,设备驱动程序和中断处理程序是直接和硬件打交道的。所以如果直接涉及到硬件细节相关的一些操作的话,那么肯定是由下面这两层完成的。那除此之外的功能基本都是在设备独立性软件来完成的。

    那除了这儿提到的内容之外,大家也需要对逻辑设备表LUT它的功能有一个大体的印象。

  • 相关阅读:
    【Java面试题】52 java中会存在内存泄漏吗,请简单描述。
    【Java面试题】51 什么时候用assert。
    JS 生成GUID 方法
    最全html5 meta设置详解 (转)
    前端开发工程师 调试方法合集
    [超级懒人最简单法]iPhone 6 plus 适配切图方法分享(转载文章)
    H5移动前端开发常用高能css3汇总
    前端性能监控方案window.performance 调研(转)
    微信浏览器取消缓存的方法
    H5移动APP开发 细节详解(转)
  • 原文地址:https://www.cnblogs.com/ZHONGZHENHUA/p/11324386.html
Copyright © 2020-2023  润新知