zoukankan      html  css  js  c++  java
  • 【知识强化】第四章 文件管理 4.3 磁盘组织与管理

    那由盘面、柱面、扇区又引出了磁盘的物理地址结构,那最后我们会介绍磁盘的几种分类。

    在这个磁盘的中间会有一个马达,而这个马达转动的时候就可以带动整个磁盘的转动。

    那这是磁道和扇区相关的概念。

    那接下来我们来看一下怎么从磁盘当中读取数据呢?

    所以大家需要注意的是,如果要对一个扇区进行读或者写操作的话,那么需要通过磁头臂来带动磁头,让磁头放到那个扇区对应的磁道上。

    所以这是磁盘的物理地址的结构——(柱面号,盘面号,扇区号)。

    可以通过这个磁臂来回地伸缩来带动磁头,从而就可以定位到指定的磁道。

    那这是按照磁头是否可以移动来进行划分的两种分类——活动头磁盘和固定头磁盘。

    另外,如果从这些盘片到底是否可以更换又可以把磁盘划分为可换盘磁盘还有固定盘磁盘。

    理解磁道、扇区、柱面、盘面这些最基本的概念。那由盘面、柱面、扇区这些概念我们可以知道磁盘的物理地址结构可以分为这样的三元组,可以用(柱面号、盘面号、扇区号)来定位整个磁盘当中的某一个特定的扇区。那这个物理地址的结构也是很常考查的。而磁头的移动需要耗费一定的时间。总之要读取某一个扇区或者某一个磁道上的数据的时候,那我们需要移动磁头。那在这个小节的最后我们介绍了磁盘的分类,那这个大家能够有个印象就可以了。比较可能进行考查的是固定磁头磁盘当中每一个磁道都会对应一个磁头,而移动头磁盘当中每一个盘面只会有一个磁头。这一点有可能在选择题当中进行考查。

    在这个小节中我们会学习一个很重要很高频的考点——磁盘调度算法。

    那磁盘调度算法的不同会影响寻找时间的长短,所以选择一个合适的磁盘调度算法对于磁盘的整体性能来说是会有很大影响的。

    磁头是由磁头臂带动的,所以在移动磁头之前是需要先启动磁头臂,这个需要花费一定的时间。

    那这是读写操作要进行的第一个步骤,就是寻道,寻找对应的磁道。

    第二个读写操作需要花费的时间,叫延迟时间。

    那么转动磁盘所消耗的这个时间就是咱们这儿所谓的延迟时间。那从这个地方我们可以看到,磁盘的转速越高,那它的延迟时间会越小,也就是说磁盘的读写速度越快。

    那这是读写操作过程中需要花费的第二个部分的时间——延迟时间。

    第三个部分的时间叫传输时间。

    传输时间指的哪部分。那通过寻道时间还有延迟时间这两个部分之后,我们的磁头此时已经放在了想要读取的那个扇区的开头的位置。

    那接下来要读取这些扇区的数据,就需要继续转动磁盘。那转动磁盘所需要的这些时间就是所谓的传输时间。

    因此一次磁盘的读或者写操作,总共需要的时间就应该是寻道时间+延迟时间+传输时间。

    那从延迟时间和传输时间的这个计算公式大家也可以发现,转速越快延迟时间越短,传输时间也越短。操作系统不可能用软件的方式来加快这个磁盘的转速,所以延迟时间和传输时间是操作系统无法优化的两个部分的时间。

    那操作系统唯一可以影响的时间就是寻道时间。根据不同的磁盘调度算法,这个寻道时间会有很大的差异。那我们接下来会开始学习各种我们需要掌握的磁盘调度算法。

    所以先来先服务算法的优点是很公平,先来的就先得到服务。并且如果请求访问的那些磁道是比较集中的话,那么这个算法的性能还算可以。不过这个算法的缺点也很明显,如果有大量的进程竟争地使用这个硬盘,并且请求访问的这个磁道很分散的话,那么先来先服务算法的性能就会很差,它在性能上其实是近似于随机调度算法。那相应的,如果要访问那些磁道很分散的话,那么先来先服务算法的寻道时间就会很长,平均寻找长度也会很长。

    相比于之前的先来先服务算法来说,这个平均寻找长度、平均的寻道时间就会变得少了很多了。所以最短寻找时间优先算法的性能是比较好的,平均寻道时间也比较短。但是这种算法的缺点是有可能会产生“饥饿”现象。那这样的话就导致了这边的这些请求长期得不到服务的现象,也就是发生了“饥饿”。

    只要在这个小区域内有源源不断的请求到来,那么它会一直在这个小区域内,而没办法跳出这个区域为其他区域的这些请求进行服务。

    因此为了解决这个问题人们又提出了扫描算法。可见,这种算法的平均寻找时间要比先来先服务算法要好很多,虽然说这个平均寻找长度、平均寻道时间要比最短寻找时间优先算法要更次一些,但是这种算法的规则带来的好处就是它不会产生“饥饿”现象。所以在刚才咱们分析的这个场景当中,对于90号磁道的访问频率要更低一些,而对于184号磁道的访问频率要更高一些。

    那这是扫描算法可以再改进的第二个地方。那我们首先看一下第一个缺点应该怎么改进。那在之前的扫描算法的当中,这个平均寻找长度是30多个,所以采用LOOK调度算法这种策略的话,那么平均的寻找长度、平均寻道时间就进一步地得到了缩短。

    那接下来我们再来看一下扫描算法的第二个缺点应该怎么解决。所以C-SCAN算法或者说循环扫描算法它比起SCAN算法来说对各个位置的这种磁道的响应频率就变得很平均了。不过和SCAN算法类似,这种算法有一个很明显的缺点,

    那为了解决C-SCAN算法的这些缺点,人们又提出了C-LOOK算法。那C-LOOK算法和LOOK算法其实是很类似的,也是采用了同样的思想。所以比起C-SCAN算法来说,C-LOOK算法使寻道时间还有平均寻找长度进一步地缩短了。

    寻道时间由启动磁臂的时间和移动磁头所花的时间组成。那我们之后介绍的这些磁盘调度算法主要影响的是移动磁头所花的时间。需要能够自己推出来那个延迟时间和传输时间的这个计算公式。最短寻找时间优先算法有可能导致饥饿,并且这种算法它只能保证眼前最优,但无法保证总体最优。也就是说每一次的寻道肯定是当前来看最短的,不过总体来看总的寻道时间、总的寻道长度未必是最短的。那之后我们又介绍了SCAN算法,还有SCAN算法的改良性算法——LOOK调度算法和C-SCAN算法,还有C-SCAN算法的改良性算法C-LOOK算法。不过大家在做题的时候,如果题目当中没有特别地说明,那么题目中所指的SCAN算法其实就是LOOK算法,题目所指的C-SCAN算法其实就是C-LOOK算法,也就是说这个磁头并不一定需要移动到最外侧的那个磁道上,只要磁头的移动方向上没有别的请求了,那么就可以让磁头立即改变方向。

    减少磁盘延迟时间的方法。

    探讨一个磁盘地址结构的问题。

    启动磁头臂和移动磁头其实是一种物理上的移动,它需要花费的时间是比较高的。那这是盘面号在柱面号之前的这种情况。

    书里推荐的这种物理地址结构,也就是柱面号放在盘面号之前会发生什么情况呢?

    因此我们接下来想要读取这个范围内的这些扇区的话,我们只需要激活1号盘面对应的这个磁头就可以了,并不需要像之前所介绍的那样再启动磁头臂然后来回地移动磁头。

    介绍第二种减少磁盘延迟时间的方法,叫做错位命名。我们先来分析一下不采用错位命名方式在读取这种编号连续的这些扇区的时候会发生什么情况。

    那这个地方需要注意,所有的这些盘面都是一起连轴转的,都是同步旋转的。那假设此时我们要读取的是物理地址为0的扇区一直到物理地址为8的扇区,也就是一直读到这个扇区。

    那这个读取的过程应该是这样的。首先要读取的是0号盘面最内侧这个磁道所有的这些扇区,所以首先需要把0号盘面的这个磁头激活,然后让磁盘进行旋转,并且旋转的过程中进行读写。

    那通过之前的分析我们知道,它需要转两圈才可以把最内侧的这个磁道给读完。那转了两圈之后,此时磁头指向的是这个位置。那相应的一号盘面和0号盘面也是同步旋转的,所以1号盘面应该也是指向了它自己的0号扇区所对应的这个位置。

    但是之前咱们说过,

    这个磁盘继续旋转,在这个旋转的过程当中,

    由于磁头还没有准备好读写数据,因此1号盘面的0号程序,也就是物理地址为8的这个程序,从磁头下面划过的这个过程当中,并不能直接把这块的数据给读入。如果要读入这块的数据,那只能等到这个磁盘再转一圈让这个扇区再次划过磁头下方。也就是说,如果我们把这些盘面它们相对位置相同的这些扇区都设置为相同的编号,那么可能会增加延迟时间。

    所以为了解决这个问题,可以用错位命名的方式来进行。0号盘面是在1号盘面的正上方,也就是说0号盘面的0号程序它的正下方对应的应该是1号盘面的7号程序。而0号盘面的4号程序它的正下方对应的应该是1号盘面的0号程序。也就是说这些扇区的编号它们是错开的,所以这也是为什么叫错位命名的原因。

    那读取完这个区域的数据之后,这个磁头它需要有一定的时间进行准备。那在这个准备的过程中是不能读入任何数据的。

    那相比于之前的那种方案,这种方案很显然可以减少磁盘的延迟时间。那这个点其实是看书的时候不太容易理解的一个点。

    我们需要理解为什么柱面号一定要在盘面号之前,其实本质原因就是为了减少移动磁头的这个次数。

    介绍磁盘管理相关的几个很简单的知识点。

    什么是磁盘初始化,磁盘初始化的过程中需要做些什么。介绍引导块或者说起始块的概念。最后会介绍对于磁盘坏块的一个管理。

    我们的磁盘刚被制造出来的时候,其实是只被划分成了一个一个的磁道。

    那在磁盘正式出厂之前,还需要进行一个叫做低级格式化或者叫物理格式化的过程。

    这个过程就是一个划分扇区的过程。那每个扇区的数据区域所能存放的数据数量都是相同的,比如说可以存放512个字节。所以咱们之前所聊到的一个扇区可以存放数据的大小其实指的是数据区域可以存放的大小。咱们在讲文件的物理结构的时候讲过一种链式结构,也就是把文件的那些数据块用链接的方式把它链起来。那前一个数据块指向下一个数据块的指针其实就可以保存在尾部这个部分,也就是说这个链接指针并不需要占用数据区域,那这样的话可以方便操作系统管理。另外呢管理扇区所需要的那些数据结构一般也是放在头和尾的位置,比如说像扇区校验码。那大家在计组当中应该也学过奇偶校验还有循环冗余校验码等等。这些校验码可以用来检查数据区域当中存放的这些数据是否发生了错误,那这样的话可以大幅度地提高磁盘存储数据的一个可靠性。总之一个扇区由头、数据区域、尾三个部分组成。那扇区的划分其实是在出厂之前,低级格式化也就是物理格式化的时候进行的。

    那在我们正式开始使用磁盘之前,还需要对磁盘进行逻辑上的分区。每个分区呢由若干个相邻的柱面组成。

    这儿所谓的分区就是我们平时熟悉的C盘、D盘、E盘。

    包括要创建这些根目录的目录文件,并且要把那些用于存储空间管理的数据结构也进行初始化,比如说像咱们之前学过的位示图、空闲分区表等等也是在逻辑格式化这个步骤当中给建立的。那这是磁盘初始化的时候所要做的几个事情。

    什么是磁盘的引导块?

    在我们的磁盘完成了物理格式化,还有磁盘分区和逻辑格式化之后,我们就可以把操作系统的那些相关数据把它写到这个磁盘中了。也就是我们所谓的自己安装操作系统的一个过程。

    那在计算机开机启动的时候,其实是需要进行一系列的初始化工作的。包括初始化CPU、初始化内存还有初始化像寄存器之类的一些硬件部件。但是这个初始化的过程需要执行所谓的初始化程序,也叫自举程序。

    那一般来说这个自举程序是存放在ROM也就是只读存储器当中的。而ROM中的数据是在出厂的时候就已经写到了这个ROM当中,并且以后就不能再修改。

    那一般来说ROM在出厂的时候就直接集成到了我们的电脑主板上,也就是说计算机开机的时候,它首先会读取ROM当中的这个这些程序并且执行这些程序来完成初始化的工作。

    但是我们来思考一下。但是由于自举程序本身又比较复杂,所以我们不太可能保证自举程序相关的数据永远不改变。

    那这个问题怎么解决呢?那现代的操作系统一般是只在ROM当中存放很小的一个自举装入程序,

    而完整的自举程序会存放在,比如说存放在C盘的这几个盘块上。那么这些区域就可以称作启动分区或者叫引导块、启动块。启动块规定必须在磁盘的固定位置。

    那当计算机开机的时候首先会执行ROM中的自举装入程序。在执行这个自举装入程序的过程当中,CPU就可以知道接下来需要执行的自举程序是存放在硬盘当中的哪个位置的。那通过自举装入程序的引导,CPU就可以从磁盘中读取完整的自举程序。这样的话就可以完成初始化。那自举装入程序的复杂度不高,很小。所以其实是可以保证自举装入程序不出错的,不需要更改的。而自举程序需要更改的话,我们就把自举程序放在磁盘的固定位置。那这样的话当自举程序需要更新的时候,只需要重新把这些引导块当中的数据把它重写一下、更新一下就可以了。所以这种方案带来的好处就是自举程序的更新会变得很方便。

    那一般来说拥有这个启动分区或者叫引导快、启动块的这个逻辑磁盘就被称为启动磁盘或者叫系统磁盘。那像平时咱们很熟悉的C盘就是系统磁盘。也就说其实C盘的某一个固定位置是装了我们的自举程序的。那这是引导块的作用。

    操作系统是无法通过软件的方式把它进行修复的。

    所以为了防止我们错误地使用这些坏块,我们就必须把这些坏块给标记出来。那标记为坏块的这些块之后不再分配给任何一个文件就可以了。那由于操作系统在对存储空间进行管理的时候肯定是需要读取文件分配表FAT的内容的。而哪些块是坏块是记录在文件分配表FAT当中的。因此采用这种方式的话,坏块对这个操作系统是不透明的。

    磁盘控制器会负责维护一个叫做坏块链表的一个链表,顾名思义就是把这些坏块用某种方式把它们链接起来。那在磁盘出厂的时候其实就已经有可能有一些坏块了,所以在出厂之前就会对这些坏块进行一个检查,

    在低级格式化也就是物理格式化的时候就需要把这个坏块链进行初始化。

    另外,磁盘控制器还会保留一些好的“备用”扇区用来替换这些坏块。但是这个过程对于操作系统是透明的,操作系统是不可知的。那这种方案也被称为扇区备用。那这是对磁盘的坏块进行管理的两种方法。

    磁盘的初始化需要做一些什么事情,分为低级格式化(物理格式化)、磁盘分区还有逻辑格式化这样三个步骤。大家需要理解并且记住各个步骤需要做的是一些什么事情。引导块主要是用于存放自举程序的。在开机的时候一定需要运行自举程序才可以完成CPU、内存、寄存器的初始化的过程。最后我们介绍了两种坏块的管理方式。大家只需要有个印象,能应付选择题就可以了。

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

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

    了解一下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它的功能有一个大体的印象。

  • 相关阅读:
    MongoDB一键安装
    Mongo基本操作
    MongoDB AUTH结果验证及开启方法
    MongoDB AUTH结果验证
    MongoDB使用
    MongoDB-安装配置
    11204RAC-dbca建库脚本
    MySQL主从同步最佳实践
    实时抓取主从的同步状态
    守护神 Supervisor
  • 原文地址:https://www.cnblogs.com/ZHONGZHENHUA/p/11310094.html
Copyright © 2011-2022 走看看