ARM嵌入式系统开发完全入门与主流实践
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.6 I/O设备

操作系统的核心内容是内存管理、进程管理等,而I/O设备作为计算机系统与外界的交互和通信媒介,其管理和控制也不容忽视。并且,由于I/O设备千差万别,这就给操作系统的I/O设备管理和控制造成了不少的难度和麻烦。

在I/O设备中,最重要的也许就数磁盘了。磁盘作为操作系统和各类应用程序及数据文件的存储介质,其管理和调度的效率同样是操作系统性能的重要影响因素。I/O设备尽管很多,但是可以根据其信息传递的发送者和接收者的不同将其分为3类,即人机交互型、计算机系统内交互型和通信类型。其中人机交互型I/O是指用于人机交互的I/O设备,如显示器、键盘、鼠标和打印机等;对计算机系统内交互型I/O而言,所传递的信息只是计算机内部可识别的,如磁盘驱动器、传感器、控制器等;通信类型I/O则用于计算机系统与外部设备的交互,这样的I/O设备包括调制解调器、网卡等。

2.6.1 I/O设备描述参数

I/O设备千差万别,总结起来,可以看出I/O设备在6个方面显示出各自不同于其他I/O设备的特征,这在I/O设备管理和控制中需要考虑到。

(1)数据传输速率

不同I/O设备之间的数据传输速率可以相差几个数量级。例如,现在的磁盘传输峰值速率可以达到100Mbps,而键盘输入数据的传输速率可能慢到几分钟才一个字节。

(2)功能应用

I/O设备的功能界定会影响到操作系统的相关设计。例如,一个磁盘I/O需要操作系统具备文件系统管理功能。

(3)控制复杂度

打印机可能只需要很简单的控制界面,而磁盘控制界面可能就复杂得多。如何能够隐藏这些复杂性,使得使用简便,是操作系统需要考虑的事情。

(4)传输数据的计量单位

传输数据的计量单位是块传输方式还是流传输方式,都会对操作系统的设计产生影响。

(5)数据编码方式

不同的I/O设备有着不同的编码方式,这其中包括字符含义的规定和不同的奇偶校验等。

(6)出错情况

I/O设备在运行过程中出现的错误及其报告方式、错误所导致的后果,以及可能的反应都因I/O设备的不同而不同。

2.6.2 I/O技术的演变

和计算机系统的其他软、硬模块存在一个演化的过程一样,I/O技术也在不断地发展演化。从早期的由处理器直接控制到现在的I/O模块自成一体,I/O技术的演变总共可以分为6个阶段。

● 处理器直接控制外围设备。

● 可编程I/O模块引入,处理器与某些I/O设备工作的具体细节有了某种程度的分离,不过在这阶段,处理器需要在进行I/O操作的过程中挂起等待直到操作完成。

● 在可编程I/O模块的基础上引入中断,这样,处理器可以在I/O设备完成相应操作的同时去执行其他指令。

● DMA技术引入,这个时候,I/O模块可以通过DMA技术和内存直接打交道而不需要通过处理器才能完成每一次的I/O读写。

● I/O模块功能进一步增强,可以看做是一个专用的处理器,从而中央处理器得到了进一步解放。

● I/O模块功能再一次提升,开始拥有自己的独立的内存。

从以上发展趋势不难看出,I/O技术的演变过程实际上也是计算机系统的中央处理器从I/O繁重的事务处理中不断得到解脱的过程。虽然说,I/O技术已经发展到了第六阶段,但是值得注意的是,直接内存访问(DMA)技术仍然在计算机系统的I/O技术中扮演着非常重要的角色。其中第五阶段的I/O模块被称为I/O通道,第六阶段的I/O模块被称为I/O处理器,一般地,两者可统称为I/O通道。

2.6.3 I/O设备逻辑描述

前面已经看到,I/O设备在6个方面各有特色,并且I/O技术也在不断演化,可能出现即便是同一种I/O设备,却用不同技术实现的情况,这就给操作系统的I/O设备管理与控制带来了难度。一般而言,在这方面,操作系统设计需要考虑两方面的问题。一是效率问题,因为I/O设备是系统性能的瓶颈所在,效率就尤显重要;二是抽象程度问题,总是希望所有的I/O设备都能有一个统一的接口便于上层系统和应用的操作,因此,需要对各I/O设备进行某种程度甚至是多个层次的抽象以便统一管理和操作。

多道程序设计在某种程度上可以提高效率,它可以让在某些进程等待I/O处理的同时,别的进程仍然继续运行。然而,即便是这样,仍然会有瓶颈存在,那就是当进程在内存和磁盘之间进行交换(swap)的时候,需要涉及到对磁盘的频繁访问,而磁盘访问本身就是一个很典型的I/O操作,将在后面章节详细阐述相关内容。这里主要讨论与抽象问题有关的I/O设备的逻辑描述。

I/O设备逻辑描述本质上是一个层次化描述。通过对较低一层的抽象和某些细节的隐藏,从而对上层提供更抽象、更一般化的操作界面,进而达到对各类I/O设备的某种抽象层次上的统一。

按照这种层次化思想,我们可以根据各类设备的不同特点抽象出适合它们的逻辑描述形式,在图2-9中给出了最常见的3种。

图2-9 一个I/O逻辑描述形式

先来看看其中最简单的一种,就是本地外围设备I/O的逻辑描述,如图2-9(a)所示。

(1)逻辑I/O

逻辑I/O模块将I/O设备看做是一个逻辑资源,而不需要去考虑具体某个细节是怎么实现的,它提供用户进程所需的一些一般性的I/O功能接口,从而让用户进程只需要进行类似打开、关闭、读和写这样的简单命令就可以对I/O设备进行操作了。

(2)设备I/O

在设备I/O层,各种操作申请和一些数据被转换成一连串的I/O指令、通道命令及控制指令。在这里,缓冲技术可用来提高效率。

(3)调度与控制

I/O操作的排队和调度在这一层完成,同时也包括对操作的控制。在这一层中,中断被处理,I/O状态得以收集和报告,软件和硬件才开始真正打交道。

对于一个通信设备而言,如图2-9(b)所示,其逻辑描述形式和本地外围设备的很相像。最主要的不同点就是逻辑I/O模块被换成了通信架构模块,而这个模块本身可能就包含很多层,比如说TCP/IP等。

图2-9(c)给出了支持文件系统的外围存储设备I/O的一个很典型的逻辑表达形式,它有着本地外围设备I/O所没有的3个层次。

①目录管理

在这一层中,以字符串形式给出的文件名将被转换成一个通过文件描述符或者索引表格直接或间接地指向相应文件的标识。这一层也处理用户进程所发出的某些针对文件目录的操作,如创建、删除、重新组织等。

②文件系统

这一层负责文件的逻辑组织,并且会处理用户发出的某些操作申请,比如打开、关闭、读、写等,访问权限管理也在这一层实现。

③物理组织

对文件和记录位置的逻辑表达必须在适当时候转换成外围存储设备中的物理地址,并且外围存储空间的分配需要合理管理,这些都是物理组织层的任务所在。

2.6.4 I/O缓冲技术

现代操作系统引入了虚拟内存管理,这为进程管理和程序编写带来了极大方便。现在来考虑在一般情况下,进程在完成对某I/O设备的读写操作时会发生什么情况。

先来看进程在其虚拟内存中间完成读写操作的情况。当进程需要和I/O设备交换信息的时候,需要在其虚拟内存空间保留一块内存区域来容纳相应的信息,这就有可能导致死锁:当进程发出I/O操作申请之后,如果进程挂起,并在I/O操作完成之前被换出物理内存,进程就会进入阻塞状态以等待I/O操作完成,同时I/O操作也会进入阻塞状态以等待进程换入物理内存。因此在读写操作的过程中,这块区域所在的分页应当始终处于物理内存中,进而这个I/O操作就会影响正常的虚拟内存管理。

为了避免这种情况的出现,可以引入I/O缓冲技术,以此来提高操作系统性能。为便于讨论,可以将I/O设备分成两类。一类是流传输设备,也称为字符设备,在这类设备中,传输数据的最小单位是字节,并且只允许按顺序访问,一般不使用缓冲技术;另外一类是块设备,其传输数据的基本单位是可寻址的块,大多数块设备允许随机访问,而且常常采用缓冲技术。

1.单缓冲

最简单的I/O缓冲技术就是单缓冲(Single Buffering),如图2-10(b)所示。当用户进程发出一个I/O操作请求时,操作系统就会在操作系统内核所拥有的物理内存空间开辟一块连续存储区域用做缓冲内存。

图2-10 I/O缓冲技术(I/O读)

对块设备而言,一个典型的I/O读操作包含两步:首先,将一个单位的信息块读入缓冲内存,然后,这个信息块被转移到用户进程空间。这样做比起无缓冲的情况主要有两个优越性:第一,可以提高操作系统效率,信息块读取的分步进行可以带来操作的并行化;第二,进程的虚拟内存管理不会再受其I/O操作的影响,避免了出现死锁等麻烦。与此相适应,操作系统本身的设计需要增加新的内容。

假设读入一个信息块的时间是T,而相邻两次I/O读申请的时间间隔是C。可以对无缓冲技术和单缓冲技术的性能做一个粗略比较:可以计算出在无缓冲时,每个信息块所耗费时间是T+C,而在单缓冲情况下,所耗费的时间则是max(C,T)+M,其中M是将缓冲内存的信息块转移到用户进程空间所耗费的时间,通常,这个时间很少。不难看出,在单缓冲情况下,系统性能会有很大提高。

2.双缓冲

对单缓冲的一个改进就是将缓冲内存块增加一个,从而变成两个交替读入信息块,如图2-10(c)所示。这样当用户进程正从某一个缓冲内存块转移数据时,操作系统就可以同时从I/O设备将新的信息块读入另一个缓冲内存块了。

对于块设备而言,在双缓冲情况下,同样可以大致估算出读入每个信息块所耗费的时间是max(C,T)。如果C<T的话,则双缓冲技术可以提高块设备的利用率,即单位时间内其传入的信息量将会增加;如果C>T的话,则双缓冲技术可以在某种程度上减少用户进程等待新数据块读入的次数。

3.循环缓冲

双缓冲在某种程度上使得信息在I/O设备间的流动更加顺畅,但是如果进程在短时间内需要不断读取信息的话,双缓冲技术也就不够用了,需要引入循环缓冲技术。

在循环缓冲技术中,操作系统的内存空间开辟有若干个缓冲数据块,这些数据块最终首尾相接形成了一个循环链表结构,如图2-10(d)所示。在操作系统不断地从I/O设备读入信息的同时,用户进程也在不断地转移已读入的信息,如此循环往复。只要缓冲数据块的大小和个数选择恰当,不仅能够保证最终进程等待的时间降至最低,而且还能保证数据读入的先后顺序的正确性,而后者在字符设备缓冲中很重要。

上述3种缓冲技术都可以用于流设备的情况。这个时候,流设备读入信息可以以字节为单位,也可以以某个特定长度的信息块为单位,比如说以屏幕一行的信息量为一次性读写量对信息进行读写操作。

2.6.5 磁盘调度

在过去的30多年里,处理器和内存的速度增长远远超过了磁盘访问速度的增长,现在处理器的速度已经达到了GHz,而磁盘访问速度至少要比它低两个数量级,而且这种趋势似乎要保持很久。计算机操作系统、应用程序和各类数据大多是保存在磁盘中的,而且虚拟内存管理中的进程的分页往往是在内存和磁盘之间倒来倒去的。磁盘管理的性能往往是计算机系统性能的瓶颈,磁盘管理的性能受磁盘性能参数和磁盘调度算法的影响,同时也受文件系统的影响。先来看磁盘参数和磁盘调度算法。

1.磁盘性能参数

磁盘I/O操作的具体细节取决于计算机系统、操作系统、I/O控制模块的特征,以及磁盘控制电路等因素。图2-11给出了磁盘I/O的一般传输时序。

图2-11 磁盘I/O传输时序

当磁盘驱动器工作时,磁盘以一个恒定的速度在高速旋转着。现在普通的PC硬盘的磁盘旋转速度可以达到7200RPM,随着磁盘技术的发展,这个速度会更高。当需要访问磁盘的某个区域时,磁头会首先移动到相应盘面的相应磁道,然后等待磁盘旋转直到所需要的扇区到达磁头为止,这个时候就开始进行数据传输了。磁头从开始位置移动到相应磁道所耗费的时间通常被称为寻道时间(Seek Time),而后等待磁盘旋转到所需扇区所耗费的时间被称为旋转延迟时间(Rotational Time),或者也称为潜伏时间(Latency Time),寻道时间和旋转延迟时间的总和通常被称为访问时间(Access Time),数据传输所耗费的时间被称为传输时间(Transfer Time)。

其中,寻道时间Ts的计算方法如下。

Ts=m×n+s

其中,m为跨越每个磁道的平均时间,n为需要跨越的磁道的数量,s为启动时间。并且,旋转延迟时间与转速有关。比如对于一个转速为7200RPM的硬盘,盘面旋转一周的时间大约为8.3ms,那么平均而言,旋转延迟时间就是4.2ms。

传输时间T的计算方法如下。

其中,b为要传送的字节数,r为磁盘转速,N为每磁道字节数除了访问时间和传输时间外,还有若干个和磁盘I/O操作相关的时间参数。当进程提出I/O申请后,它通常是要进入一个等待进程队列以等待设备空闲,而如果设备与其他磁盘驱动器共享I/O通道的话,这个时候,仍然需要一个等待通道空闲所耗费的时间。

从上面给出的磁盘性能参数及其计算方法可知,如果要想提高一个特定磁盘的传输速率,我们只可能在访问时间上下功夫。一个理想的情况就是,需要访问的数据正好在磁头下方,如此,旋转延迟时间为零,并且所有数据都按照访问顺序放在同一磁道,且正好在磁头所在的磁道,这样的话,就可以取得最高的传输速率。如果希望磁盘能够提供较高的传输速率,则需要磁头的位置距离下一个待访问数据尽可能近,这其中不仅包括磁道距离,而且也包括扇区距离。磁头的这个距离带有随意性,因此,磁头总是要做频繁移动,从而使得访问时间大大增加。

2.磁盘调度算法

在多道程序设计计算机系统中,每一个I/O设备通常会有多个I/O申请形成队列。在这种情况下,如何选择下一个要处理的I/O申请对磁盘传输速度有很大影响,而每一种选择策略就是一个磁道调度算法。

如果选择是随意的,那么每次访问的磁道的分布也具有随机性,从而导致磁头寻道时间大大增加,导致磁盘传输性能大大降低。一个很公平的选择方法就是先入先出规则(FIFO),即最先提出的I/O申请最早得到处理。这种选择策略在只有少数几个I/O申请并且所有申请访问的扇区属于扎堆的情况下,能取得比较好的性能,但是如果I/O申请很多,并且访问的磁盘区域比较随机的话,那就需要寻找更好的选择策略。下面介绍7种这样的选择策略。

(1)遵从I/O申请进程优先级原则(PRI)

对于一个基于优先级的操作系统而言,磁盘调度将受制于系统的进程调度规则,这也是从系统整体性能来考虑的。

(2)遵从进程的后进先出原则(LIFO)

有的时候,最先处理最新到达的任务往往会取得一些令人意想不到的结果。比如说在事务处理系统中,将设备提交给最新用户往往能减少磁头的移动量。FIFO、PRI及LIFO调度原则都是仅仅从I/O请求的申请者的角度提出来的,如果我们知道任何时刻磁头的位置的话,那么我们就可以从访问目标的角度出发来设计一些算法。下面将给予阐述。

(3)遵从最短服务时间最先处理原则(SSTF)

SSTF调度原则在选择下一个待处理的I/O请求的时候,会首先选择一个起始访问位置与磁头所在位置相距最近的I/O申请,这样可以保证每次磁头总能花费最少的寻道时间。当然,每次保证最少的寻道时间并不能保证最终平均寻道时间最短,但是这种调度方法却能提供比FIFO更好的性能。

(4)反复单向扫描(SCAN)

一般对于某个I/O设备而言,会不断有新的I/O申请加入其等待服务队列。在这种情况下,上面几种磁盘调度算法都可能导致某个I/O申请始终被晾在一边不被搭理,只有当队列只剩下自己时,这样的申请才会被处理。

SCAN调度算法能避免这一点,其基本算法流程是:磁头在启动后一直按照某一个固定的方向(按照磁道号排序升或者降)移动,而在申请等待队列中的一些访问目标位置正好位于这个方向上的申请则被依次处理,直到磁头到达终点(磁道号最大或者最少),又开始反向移动。在移动过程中,按照正向移动的方法对申请等待队列中的I/O申请进行处理,并在到达原来起点之后再重复开始的过程,如此周而复始地按顺序扫描磁盘表面,并对符合条件的I/O申请进行处理。

(5)单向扫描原则(C-SCAN)

C-SCAN调度算法只允许磁头单向扫描盘面,当到达终点后,快速返回起点,然后继续原来的扫描过程。在扫描过程中,同时检查I/O申请等待队列中的申请,只有发现其访问位置处于磁头位置的时候才加以处理。如此周而复始,完成磁盘调度任务。

(6)N步单向扫描原则(N-Step-SCAN)

SSTF、SCAN和C-SCAN调度算法都可能出现一个很让人恼火的事情,即如果某个磁道一时间有很多I/O申请的话,就会导致磁头移动到该磁道后长时间不能解脱,从而导致其他申请需要做长时间等待。

N-Step-SCAN调度算法的提出就是为了克服这一现象。这种算法的基本内核仍然是SCAN的,所不同的是,原来的I/O申请等待队列被分成长度为N的一个个子队列。SCAN可供选择的I/O申请是这些子队列中的某一个,一旦选中,就会不断执行直到整个队列处理完,然后转向下一个子队列继续同样的操作。在处理过程中,难免会有新的I/O申请加入,在这个时候,新的I/O申请不能加入到正在处理的子队列中,即便它的长度可能已经小于N了,它只能要么加入到其他某个长度小于N的未处理子队列中,要么形成一个新的子队列。

(7)FSCAN

FSCAN调度算法的基本思想和N-Step-SCAN算法类似,所不同的是在该算法中,只有两个I/O申请等待队列。当某一个队列开始处理时,另外一个队列为空,所有新加入的I/O申请都会进入这后一个空队列。而对前一个队列的处理直到所有申请都处理完毕之后,才转入第二个队列的处理。此后,所有新加入的申请将会被压入前一个队列,如此循环。表2-5给出了几种常用的磁盘调度算法比较。

表2-5 磁盘调度算法比较