514_I-O 软件层次结构
# 5.1_4_I-O 软件层次结构
各位同学大家好,在这个小节中我们会学习 IO 软件层次结构相关的知识点, IO 软件的层次从上至下依次可以分为用户层、设备独立性软件、设备驱动程序、中断程序这样的 4 层。
最下面的这 1 层当然是 IO 的硬件设备,而而 IO 设备的硬件又是由机械部分和电子部分组成的,所以 IO 硬件的组成还有它的原理,咱们在 IO 控制器小节当中进行讲解过。
这个小节中我们重点关注的是软件层面要实现的一些功能。这个地方大家会发现用户层软件和下面这三层的颜色是不一样的,因为下面这三层其实是属于操作系统内核部分,所以这三层也可以被称作 IO 核心子系统,因为它在内核部分或者简称 IO 系统,上面的用户层软件是在操作系统内核之外的,也就是可以在用户态下实现的一系列的功能。
从示意图中我们也可以看到,在这些层次结构当中,越靠近上层的其实是越接近用户的,而越靠近下层的是越接近硬件的,每个层次都会使用他们下面一层软件所提供的功能,并且向它上层的软件提供一些服务,像这种每一层使用下一层的服务来实现某一些功能,并且向上一层提供一些更简单易用的接口,这种思想这种设计方式,也在我们计算机网络的层次结构当中要使用这种思想。
当用户发出一个 IO 请求的时候, IO 请求会从上至下经过各个层次进行处理,最后被扔给 IO 硬件来执行实际的 IO 操作。当 IO 硬件做完这次 IO 操作,发出 IO 应答的时候,又会由这些层次从下往上依次进行处理,最后返回给用户。
接下来我们按从上至下的顺序依次分析一下各个层次所需要实现的功能。
# 用户层
首先来看用户层需要实现什么功能。用户层作为最接近用户的一个层次,它肯定是需要向用户提供一些简单易用的交互的接口。一般来说用户层软件,会向用户提供一些与 IO 操作相关的库函数,让用户调用这些库函数来来对设备进行操作。比如说咱们很熟悉的 c 语言里的 printf 也就是在显示屏 IO 设备上打印输出 hello world 这样一句简单的代码,其实这个 printf 库函数就是由用户层软件提供的,
既然需要使用 IO 设备进行输出操作,所以用户层软件肯定需要请求操作系统提供服务,因为只有操作系统才有对硬件操作的权利,因此用户层软件会使用设备独立性软件这一层向上提供的系统调用接口来请求操作系统内核的服务。
比如说 printf hello world 这样一句代码,在用户层软件处理完了之后,会把它翻译成等价的 write 系统调用。当然在进行调用的时候,它也会填入相应的参数,比如说要打印输出的内容,hello world 这个字符串,
所以其实设备独立性软件向上层提供了系统调用的接口,那设备独立性软件又是用来处理这个系统调用的一个层次。因此在有的题目当中,它也会把这一层称为系统调用处理层,这点大家稍微要注意一下,
像咱们很熟悉的 windows 操作系统都会向外提供一些系统调用,但是由于系统调用太低级了,还需要把它进行进一步的封装,才更方便用户使用。所以 windows 操作系统又会在用户层进行进一步的封装,然后向用户提供一些库函数接口,有兴趣的大家可以去搜索一下 windowsAPI、 windowsAPI 就是用户层软件向用户提供的库函数接口,这是用户层软件需要做的事情。
# 设备独立性软件
接下来我们来看一下设备独立性软件要实现一些什么功能?设备独立性软件又被称为设备无关性软件。正如它的名字说的这样,基本上与设备硬件特性无关的那些功能几乎都是在这一层实现的。
这层要实现的第一个功能就是咱们刚才提到的,它要向上层提供一个统一的调用接口,比如说像 write 等等这一系列的系统调用。
第二个功能要实现设备的保护,其实对设备的保护原理是类似于对文件的保护的,在很多操作系统当中,设备会被看作是一种特殊的文件,而不同的用户对各个文件的访问权限是不一样的。既然设备被看作是一种特殊的文件的话,相应的不同的用户对设备这种特殊的文件的访问权限肯定也是不一样的。因此操作系统需要提供设备保护的功能,咱们在之后的小节中还会细聊。
第三个要实现的功能叫差错处理,其实就是对设备产生的一些错误进行处理,这个地方大家能有个印象就可以了,不需要了解差错处理的细节,因为差错的类型实在是太多了,所以对差错的处理基本上不太可能进行考察。
第四个要实现的功能是设备的分配与回收,什么叫分配回收?经过之前的那一系列学习,大家应该能有一个直观的体会,因为很多设备它其实是一种临界资源,不可以同时分配给多个进程使用,所以操作系统当然需要对设备这种资源进行分配与回收的管理,咱们在之后也还会细聊。
第五个要实现的叫做数据缓冲区管理,数据缓冲区其实是用来屏蔽各个设备之间数据交换单位大小,还有传输速度的差异的。关于缓冲区管理应该怎么实现,这个很容易在选择题当中进行考察,不过这先不展开细聊,咱们在之后的小节中还会专门讲解。
第六个设备独立性软件需要实现的功能是建立逻辑设备名到物理设备名之间的映射关系,并且还需要根据设备的类型来选择调用相应的驱动程序。什么叫逻辑设备名、物理设备名?所谓逻辑设备名就是用户在请求使用一个设备的时候所提供的名字,也就是用户所看到的设备名,
比如说像咱们平时去学校打印店去打印的时候,因为打印店里的那些电脑都会连上很多台打印机和复印机,所以我们在要打印输出的时候,大家下次可以注意观察一下,它会让你选择要使用哪一台打印机或者哪一台复印机进行打印输出
我们看到的那些什么打印机一打印机二,这些我们用户看到的设备名其实是逻辑上的设备名。操作系统对这些设备进行管理,在背后还会有一个叫做物理设备名的东西。所以当我们选择某一个逻辑设备的时候,操作系统当然需要知道这个逻辑设备具体对应的到底的是哪一个物理设备
一般来说映射关系是通过一个叫做逻辑设备表的东西来实现的,英文缩写是 LUT
并且在这个逻辑设备表中还会记录每一个,逻辑设备对应的设备驱动程序的入口地址,这个地方大家会看到,这的逻辑设备名竟然还带有一个类似于文件路径的东西,其实这就是咱们刚才解释的情况。很多操作系统都会把设备当作是一种特殊的文件,所以这个文件当然也会有一个存储的路径,那每一个表项记录了逻辑设备名到物理设备名的映射关系,并且还记录了这个设备它所对应的驱动程序的入口地址是在什么地方,
一般来说会用两种方式来管理这个逻辑设备表 LUT,第一种方式就是整个系统只设置一张 LUT,但是由于各个用户在使用设备的时候,使用的都是逻辑设备名,而操作系统又是根据逻辑设备名来查找 LUT 的表项的,所以如果不同的用户使用相同的逻辑设备名的话,那么就有可能会导致逻辑设备到物理设备映射紊乱的问题,所以其实整个系统只设置一张逻辑设备表,这种方式只适合于单用户的操作系统。
第二种方式就是为每一个用户设置一张逻辑设备表,采用这种方式的话,不同用户所使用的逻辑设备名可以是重复的,并且相同的逻辑设备名可以被映射到不同的物理设备上去。
其实这两种方式有没有发现,它就有点类似于我们在文件管理当中学到的单级目录和两级目录这样的区别。在文件系统中如果采用的是单级目录结构的话,那么不同用户的文件名是不允许相同的,但是在两级目录的结构下,不同用户的文件名可以是相同的,这个地方为每个用户设置了一张逻辑设备表,其实在本质上就有点类似于设置了两级目录这样的结构。所以其实这两种方案和我们之前学过的知识点是有一些内在联系的,
刚才我们聊到了不同类型的设备需要有不同的驱动程序,这是为什么呢?比如说我们在网上搜一下打印机,大家会发现会有各种品牌的,各式各样的打印机,不同的打印机,它们的外形不同,并且其实我们看不到的内部的那些电子部分,也有可能是不同的结构。
假设佳能品牌的打印机,它内部的电子部件是这个样子的,它总共有两个数据寄存器,然后它的状态寄存器当中如果存放的是 0 的话,那么是代表这个打印机此时是空闲的,如果存的是一的话代表它是忙碌的。
但是换一种打印机,比如说是惠普的打印机,那么它内部可能就只设置了一个数据寄存器并且,它在状态寄存器当中,如果存一的话是代表设备空闲,存 0 的话是代表它忙碌,也就是刚好和佳能的 01 所代表的意思刚好是相反的。
所以通过这个例子我们会发现不同型号的这些设备,他们内部的那些电子部件有可能是完全不一样的,各个设备内部的硬件特性肯定只有厂家他们的设计者才可以知道,所以如果操作系统要通过这些设备的控制器来控制这些设备的具体运行的话,那么操作系统肯定是需要了解这些设备内部的硬件细节的,不过这些 IO 设备多种多样,所以操作系统不可能了解所有的这些设备的内部细节,因此这些设备在出厂的时候,一般来说厂家会提供一个与设备对应的所谓的驱动程序,然后当要控制某一个设备的时候,CPU 其实只需要执行这个设备相对应的驱动程序,就可以完成对这个设备控制器的控制了。比如说像设置控制器里边的寄存器,或者检查每个设备的状态寄存器,这些工作就是可以通过执行驱动程序来完成。
所以大家在生活中可能会发现,比如说我们买了一个机械键盘或者买了一个雷蛇的鼠标,在这些设备第一次被插上电脑的时候,我们电脑的右下角总是会有一个正在安装驱动程序,这样一个小弹窗小提示自动安装的驱动程序,其实就是由厂家提供的。
为了让操作系统实现对新的设备硬件进行具体控制的一个程序,所以其实设备独立性软件不可以直接操纵硬件,他必须调用厂家提供的设备驱动程序,由这个设备驱动程序来完成对硬件的具体控制。比如说设置设备里的各种寄存器等等这些操作,像各个类型的设备驱动程序,一般来说在系统中会以一个独立进程的方式运行存在。
所以我们再回到刚才的逻辑设备表,为什么不同的设备需要对应不同的驱动程序呢?就是因为各种设备内部的硬件特性是不一样的,因此必须执行与它对应的特定的驱动程序,才可以正常的完成对这个设备硬件的控制。
所以比如说我们要使用的是打印机一的话,那么操作系统查询这个表之后就知道接下来要执行的设备驱动程序的入口地址是这个地方,于是接下来就可以根据这个地方记录的信息来找到它对应的设备驱动程序了,这是设备驱动程序这一层需要完成的一个事情。
# 中断处理程序
接下来我们看最下面一层中断处理程序,中断相关的概念和知识,咱们的第一章已经学过很多了,所以这个地方其实相当于是在复习,当我们的硬件设备完成了 IO 操作之后,它会发出一个中断信号作为 IO 应答,那系统会根据中断信号的类型来找到与中断信号对应的中断处理程序,然后执行这个程序进行中断处理。
中断处理程序对中断的处理流程是这个样子的。
首先中断处理程序会从 IO 控制器或者说设备控制器当中读出设备的状态,来判断这次的 IO 是不是正常的结束。
如果此次是正常的结束,接下来中断处理程序会从设备控制器的数据寄存器当中读出一个字的数据,并且经由 CPU 然后放到内存缓冲区当中,这就完成了一个字的读入。
而如果此次 IO 是非正常结束的,也就是中间发生了什么意外,比如说像硬件故障之类的,那么系统会根据异常的原因做出相应的处理,所以这就是中断处理程序所需要做的一件事情。
当中断处理程序,把这一次要输入的数据放入到内存之后,接下来又会交由设备驱动程序对这些数据进行进一步的处理。
等设备驱动程序处理完了,它又会交由再上一层的设备独立性软件进行再进一步的处理,最后一层一层往上,然后一直返回给用户。所以如果要输入一个数据的话,那么对于这个数据的处理应该是从下往上依次层层处理的。
从刚才的讲解中我们也发现,除了设备驱动程序会直接和硬件打交道之外,其实中断处理程序它也需要直接和硬件打交道,但是在网上的设备独立性软件和用户层软件就不会直接和硬件打交道了。
# 小结
那么这个小节我们对 IO 软件的层次结构进行了从上至下的分析,这个地方给出了一个简要的总结,就不再赘述。
要完成一个 IO 操作,除了软件的支持之外,肯定也离不开硬件,硬件才是执行具体的 IO 操作的部件,它由机械部件和电子部件组组成,这是咱们之前的小节谈到过的内容,如果一个用户发出 IO 请求的话, IO 请求会由上至下依次由这些各个层次进行处理,最后扔给 IO 硬件实行具体的 IO 操作。
而如果硬件完成然后操作的话,它又会发出一个中断信号,接下来会由这些软件层次由下至上的层层处理,然后最后返回给用户。
这个小节的内容最常考的其实是各个层次之间的顺序,比如说他经常会问一个 IO 请求的处理次序到底是什么样的,或者已发生 IO 应答的时候,每一层软件的处理顺序依次是怎么样的,所以大家需要理解并且记住这些各个层次的从上至下的这种顺序。
另外呢也有可能考察各个层次要完成一些什么功能,大家需要抓住一个最重要的特点,设备驱动程序和中断处理程序是直接和硬件打交道的,所以如果直接涉及到硬件细节相关的一些操作的话,那么肯定是由下面的两层完成的。除此之外的功能基本都是在设备独立性软件来完成的。除了这儿提到的这些内容之外,大家也需要对逻辑设备表 LUT 它的功能做有一个大体的印象。