输入输出系统
IO系统的功能、模型和接口
-
IO系统的基本功能
- 隐藏物理设备细节:对设备进行抽象,能使用简单的读写命令操控设备
- 与设备的无关性:将设备与操作对象解耦,可以使用抽象的逻辑设备名来使用设备,提高OS的可移植性,能够执行安装并寻找驱动程序等等
- 提高处理机和IO设备的利用率:IO设备和处理机能够并行运行,不但即时响应,也减少处理机对IO设备的干预
- 对IO设备进行控制:可以用不同的控制方式:轮询,中断,DMA,通道对IO设备进行控制。
- 确保对设备的正确共享
- 错误处理:在低层就进行错误处理,当低层解决不了时,才向上上报
-
IO系统的层次结构和模型
总共分为四个层次


- IO系统接口:IO系统与上层系统之间的接口。向上层提供抽象IO命令。
- 设备独立性软件:实现设备无关的软件,设备命名,设备分配,数据缓冲和数据高速缓冲。
- 设备驱动程序:进程与设备控制器之间的通信程序,将抽象IO请求转换为对IO的具体命令。
- 中断处理程序:与硬件直接交互,完成中断IO任务。
- RW/HW接口:软件/硬件接口。
-
IO系统接口
-
块设备接口
块设备:以数据块进行存取和传输的设备,传输速率高且可寻址,如磁盘,光盘
块设备接口管理块设备的IO,并隐藏磁盘的二维结构,看似一维,并将抽象命令转换为低层命令。
-
流设备接口(字符设备接口)
流设备:以字符进行存取和传输的设备,传输速率低且不可寻址
流设备接口管理流设备IO,建立字符缓冲区来按顺序存取的方式读取,使用通用的in-control命令来调用字符设备。并且由于是独占设备,通过打开和关闭操作实现了互斥方式共享。
-
网络通信接口
提供面向网络的功能。
-
IO设备和设备控制器
一般将IO设备的机械部分作为一般的IO设备,而执行控制IO的电子部件称为设备控制器。
-
IO设备
-
分类
按使用特性分类:存储设备与IO设备(输入设备、输出设备和交互式设备)
按传输速率分类:低,中,高速设备
-
设备与控制器之间的接口
设备会通过三根线与控制器相连。数据信号线用于传输数据,并存储在控制器的缓冲区中,在转换后再传给CPU,反之亦然。控制信号线用于传送控制信号。状态信号线用于传送指示当前IO设备状态的信号。
-
-
设备控制器
设备控制器接收CPU指令,将其从繁杂的设备控制事务中解脱出来。其可以控制一个或多个IO设备,每一个IO设备对应一个地址,CPU访问时也是按照这个地址去指示的。
其有着接收和识别命令,数据交换,标识和报告设备的状态,地址识别,数据缓冲区和差错控制(根据传来数据的差错检测码检测并报告CPU)的功能。
组成:

通过数据线,地址线和控制线与CPU交互,数据线与数据寄存器和控制状态寄存器相连,地址线选择IO设备,控制线传输命令。而IO逻辑部分会通过地址线和控制线的内容选择对应设备并操作。
-
内存映像IO
为了让IO设备能被操控,需要一系列指令,一般有两种方式。
-
利用特定的IO指令
为每一个IO设备中的控制寄存器分配一个特定的IO端口,并用专门的IO指令去操作IO设备,这种会导致有两种指令,比较麻烦。
-
内存映像IO
不再区分控制寄存器的地址和内存地址,当地址大于k时就是控制寄存器地址,这样就可以用一般存取命令来操作IO设备。

-
-
IO通道
虽然控制器已经承担了一部分CPU的责任,但当IO设备很多时,CPU负担仍然比较重。因此增设IO通道将IO操作的组织,管理和结束处理也都承担,当完成时才中断通知CPU。
IO通道就是一种特殊的处理机,有执行IO指令的能力,并执行通道程序来控制IO。但其指令单一并且与CPU功能内存,没有自己的内存。
- 字节多路通道:有许多子通道,每一个子通道连接一个IO设备,其按时间片轮转连接主通道将数据传给主通道,最终主通道接收到的数据就是,但只能适用于接的是低速设备,如果是高速设备就容易丢信息。
- 数组选择通道:仅仅有一个分配型子通道,同一时间只能执行一道通道程序,当某台设备占据后,直到其结束,即便其中间有空闲,也不会被其他设备抢占。
- 数组多路通道:结合了字节多路通道和数组选择通道的优点,有多个非分配型子通道,能够同时连接多台高、中速IO设备。
由于通道和控制器的昂贵,因此其数量较少。当其分别管理几个设备的时候,同一组的设备如果需要同时使用,就会产生冲突:瓶颈。因此可以增加设备到主机间的通路,即每一个通道或者每一个控制器都能控制任意一个IO设备,这样就解决了瓶颈,并提高了系统可靠性。

中断机构和中断处理程序
-
中断简介
- 中断:指CPU对IO设备发来的中断信号的响应(暂停去执行中断处理程序),由外部设备引起,一般称为外中断。
- 陷入:指CPU内部由于程序出错产生的中断,一般称为内中断。
- 中断向量表:一般每一个IO设备对应一个中断处理程序,为了方便,将每个设备的中断请求规定一个中断号,在中断向量表中与其对应的中断处理程序的入口地址一一对应。
- 中断优先级:根据不同的中断信号分为不同的优先级
- 对多中断源的处理方式
- 屏蔽中断:当处理中断的时候,就屏蔽所有其他中断,当完成本中断后再检查是否有其他中断,实现简单,但不够实时
- 嵌套中断:当有多个中断请求时,响应优先级最高的一个,并且即便在中断程序中,高优先级中断可以抢占低优先级中断,更加复杂,但是更加实时。
-
中断处理程序
在每一次CPU指令执行完时检查是否有未响应的中断信号,如果有则停止原进程,保护被中断进程的CPU环境在中断栈中(包括处理机状态字PSW,程序计数器PC和所有CPU寄存器)。接着对每一个中断源进行测试,确定本次中断设备,然后像其发送确认信号(使其取消中断信号),并将其处理程序地址放在PC中。在执行完中断后,判断其时候正常结束,如果不正常结束,就做相应处理。最后恢复CPU现场并退出中断,而若是使用了屏蔽中断,则返回被中断进程,否则则要依据时候有优先级更高的中断请求。
而若是IO过程中发生了错误,则要向调用者报告。
设备驱动程序
进行与CPU通信的转换中介。
-
设备驱动程序概述
功能:接收命令和参数并进行转换;检查请求合法性,设置设备工作方式;根据情况发出或者使IO命令在设备队列上等待;及时响应设备控制器发来的中断请求
特点(与普通程序的区别):实现的功能是转换命令;与IO设备,IO设备采用的控制方式紧密相关;由于与硬件紧密相关,因此其中一部分需要使用汇编;允许可重入,一般会在一次调用完成前被再次调用。
设备的三种处理方式:为每一类设备设置一个进程;整个系统设置一个IO进程;不设置进程,为各类设备设定设备驱动程序供调用。
-
设备驱动程序的处理过程
将抽象要求转换为具体要求(部分必要的具体要求的信息存储在设备控制器中的寄存器中,结合抽象要求和寄存器才能得出具体要求);对服务请求进行校验(即请求时候可以执行,功能或权限上的问题);检查设备状态,看是否处于就绪状态,否则等待;传递必要参数,一般有两种参数分别放在两种寄存器中,命令寄存器(存放控制命令),方式寄存器(存放传输的方式相关数据);启动IO设备。启动后就将自己阻塞,直到设备控制器发来中断信号。
-
对IO设备的控制方式
-
轮询的可编程IO方式
每一个设备在状态寄存器中设置一个忙闲标志busy,当在接收到一条IO指令后,就将busy设为1,当完成后,输入完一个字后就设为0。在其IO的过程中,CPU不断轮流询问每个IO设备,直到其busy为0,接着才检查,读缓冲并写入存储器,对CPU造成了极大的浪费。
-
中断的可编程IO方式
在发送完IO指令后,就继续自己本身的任务。当IO设备完成任务时,向CPU发送中断信号,CPU便来进行后续处理。
-
直接存储器访问方式
上述的中断方式是以字节为单位进行传输的,每一个字节就要中断一次,而DMA的数据传输基本单位就是数据块,CPU仅在一开始和结束时干预,数据被直接送入内存。
CPU向DMA发送一条指令,DMA将指令存储在命令/状态寄存器CR中,将要数据块在内存中的地址放在内存地址寄存器MAR中,将数据块长度放在数据计数器DC中。每次DMA从磁盘读入或写出一个直接到数据寄存器DR中,DMA就会将其送入或取出到内存,并将DC-1,MAR+1。循环直到DC为0,并对CPU发出中断请求。
-
IO通道控制方式
上述的DMA方式是以数据块或连续数据块为单位进行传输的,但如果要传输多段数据块就需要多次中断,而IO通道就不需要,其单位是一组数据块。在接收到IO命令后就将其转换成对应的通道命令,包括操作码,内存地址,计数,通道程序结束位P(为1代表通道程序结束),记录结束标志R。
一条记录就是一组数据块。
-
与设备无关的IO软件
-
概述
原本需要通过设备的物理名称来直接使用设备,但是,这种情况下,如果设备忙碌,就会陷入阻塞,而与之拥有同功能的其他设备却也不能使用。而现在引入逻辑设备,一个逻辑设备可以对应多个物理设备,当一个忙碌的时候,使用这个逻辑设备也会自动切换到另一个物理设备使用,除非全部阻塞,否则程序就不会被阻塞。
并且可以实现IO重定向,由于程序只和逻辑设备绑定,因此我们可以不更改程序而只更改逻辑设备绑定的物理设备来切换物理设备。
而为了实现逻辑设备,就需要建立一张逻辑设备表,能够将逻辑地址转换为物理地址。
-
功能
设备驱动程序的统一接口:使添加一个新的设备变得容易,并且便于管理,并可以将抽象的设备名能够转换为具体的设备名。
缓冲管理:IO速度远慢于CPU,要进行缓冲。
差错控制:设备很容易出故障,无论是暂时性错误(可以通过重试来解决)还是持久性错误,需要向操作系统报告。
对独立设备的分配与回收:为了避免对独占设备的争夺,由其对独占设备进行统一分配。
独立于设备的逻辑数据块:各种不同的设备每次传输的单位是不同大小的,而设备独立性软件能够隐藏这些差异,向高层软件提供统一的逻辑数据块。
-
设备分配
管理设备需要有数据结构。
-
设备控制表DCT
每一个设备都有一张DCT。包括设备类型type,设备标示字段deviceid,设备队列队首指针(等待请求本设备的进程的列表),忙闲标志,指向控制器的指针,重复执行次数(IO设备容易出错,要多执行,除非到达执行次数仍然失败才失败)
-
控制器控制表COCT、通道控制表CHCT和系统设备表SDT(记录了所有设备情况,包含DCT)

-
设备分配时应该考虑的因素
- 设备的固有属性:独占,共享,虚拟
- 设备分配算法:先来先服务FCFS或者优先级
- 设备分配中的安全性:一种是安全分配算法,一分配到IO设备,就让进程陷入阻塞,很安全,但这样是顺序执行,效率低。另一种是不安全分配方式,分配到IO设备后仍然可以继续运行,并继续请求其他设备,效率较高,但可能产生死锁,需要进行安全性计算。
-
独占设备的分配程序
每次分配设备,首先先根据物理设备名查找SDT,找到其DCT,并判断时候忙,忙则加入等待,否则计算安全性,然后请求其对应的控制块,忙则入队,最后请求通道,忙则入队。
但上述的这一过程不具备设备无关性,因此实际上应该用逻辑设备名去查找SDT,一个一个去试属于这个逻辑设备名的SDT,直到找到,并计算安全性。
-
-
逻辑设备名到物理设备名的映射的实现
-
逻辑设备表LUT(Logical Unit Table)
每行包括三项:逻辑设备名、物理设备名(可以有多个)和设备驱动程序入口地址,当一个新的IO设备被加载的时候,就会在LUT中添加上一项,而其逻辑设备名则是根据设备类型自动生成的。
-
逻辑设备表的设置问题
一种是只设置一张LUT,但是不允许具有相同的逻辑设备名,因此对多用户系统的时候,不同的用户要使用同一个设备就需要使用不同的逻辑设备名就很麻烦。
另一种是由于每个用户都有一个进程,为每一个用户的PCB中都建立一张LUT。
-
用户层的IO软件
-
系统调用和库函数
-
系统调用
请求IO显然是核心态的操作,而应用程序是用户态的,因此OS在用户层引入了系统调用来间接进行调用。在实际情况中,当应用程序要请求IO,就使用系统调用命令,当OS检测到系统带哦用命令时,就切换到核心态,执行IO,在执行完后再切换回用户态继续运行。
但实际上,所有的OS提供的服务应用程序都需要通过系统调用来间接使用,然而,大部分系统调用都是汇编指令,因此比较麻烦。
-
库函数
为了方便程序调用,一些高级语言都实现了与系统调用一一对应的库函数,在编译为二进制程序后相关系统调用也将被嵌入到其中。库函数使得用户能够方便的取得操作系统的服务。
-
-
假脱机系统(Spoolling)
可以将一台物理IO设备虚拟化为多台逻辑IO设备。
-
原理(需要高速外存的支持)
实际上就是模拟以前的脱机系统,首先所有的进程在请求这个设备的时候并没有真正的占用这个设备,而只是通过井管理程序将输入输出任务放在了输入井和输出井这两个缓冲区中,而真正的执行则是由输入输出程序独占IO设备,一个一个对井中的任务执行。

两个缓冲区是为了匹配cpu和磁盘,磁盘和IO设备之间的速度。
-
特点:提高了IO设备,将独占设备改造为了共享设备,实现了虚拟设备功能,所有使用这个IO设备的程序都会认为自己独占了这个IO设备。
-
守护进程
可以用守护进程来代替假脱机管理进程,守护进程根据假脱机目录中的一个一个请求申请内存空间,进行IO并将地址返回。而其他程序要调用这个IO设备就是将任务放到假脱机目录中。
凡是需要将独占设备改造为共享设备时,都要为该设备配置守护进程和假脱机文件队列。
-
缓冲区管理
目的:组织好这些缓冲区,并提供获得和释放缓冲区的手段
-
缓冲的引入的原因:缓和设备速度不匹配的问题;减少对CPU的中断频率,积累一点再一起提交;解决数据粒度不匹配的问题,生产者和消费者的单元数量不一致,可以通过缓冲达到统一;提高CPU和IO的并行性
-
单缓冲区和双缓冲区
对于生产者和消费者的情况,每一块的处理事件为。

但这种情况下,输入和传送是不能同时进行的,因为缓冲区是共享资源,是互斥的。因此如果消费者还未取出缓冲区中的数据,生产者就阻塞了。因此使用双缓冲区机制。
首先输入的时候,先输入第一缓冲区,当第一缓冲区满后,再往第二缓冲区输入,而此时,消费者从第一缓冲区接收,生产者和消费者总是独占不同的缓冲区,实现了不阻塞。
当C<T时,设备可以连续输入,C>T时,CPU不必等待设备输入。

而对于两台机器之间互相通信的时候,如果只配置单缓冲,则任意时刻都只能单方向的数据传输,因为传输的时候同时占据两边的缓冲区。因此要用双缓冲区。

-
环形缓冲区
当输入和输出的速度基本匹配的时候,双缓冲区的效果很好,但是如果速度差距很大,那么增加缓冲区数量可以有所改善。
一个环形缓冲区中包含多个大小相同的缓冲区,缓冲区有三种状态,空缓冲区R,已装满缓冲区G和CPU正在使用的缓冲区C。有三个指针来指示,分别是下一个可用缓冲区G的Nextg指针,下次可用空缓冲区R的Nexti指针和当前正在使用的缓冲区C的Current指针。
总共有两种操作。Getbuf,输入则获取下一个G,输出则获取下一个R,并调整指针。Releasebuf释放缓冲区C,输入则改为R,输出则改为G。
正常情况下,三者的顺序是按某一个方向Nexti,Current,Nextg,但是如果Nexti追上了Nextg,则说明输入速度快于处理速度,系统受计算限制。如果Nextg追上了Nexti,则说明处理速度快于输入速度,系统受IO限制。
-
缓冲池
上述的缓冲区都是针对特定的生产者和消费者设置的,属于专用缓冲。当系统较大时,会有很多这样的专用缓冲,不仅消耗空间利用率还不高。因此一般用缓冲池:是一个管理缓冲区的数据结构。
-
缓冲池的组成
每个缓冲区由缓冲首部(缓冲区号,设备号,设备上的数据块号,同步信号量和队列链接指针)和缓冲体(存放数据)组成。
一般将相同类型的缓冲区链接在一起方便管理:空白缓冲队列emq,输入队列inq,输出队列outq。用F(队列)和L(队列)来表示其队首队尾。
除此之外还包括分别用于收容输入数据,收容输出数据,提取输入数据,提取输出数据的工作缓冲区。
-
操作
与普通队列一致,但是由于缓冲池是临界资源,因此Getbuf和Putbuf需要是进程同步且互斥的。用两个信号量来处理,RS代表缓冲区中时候有资源,MS则用于互斥。


-
缓冲区的工作方式
收容输入:Getbuf(emq),给予一个进程使用放输入数据,Putbuf(inq,hin)
提取输入:Getbuf(inq),给予一个进程使用读取输入的数据,Putbuf(emq,sin)
收容输出:Getbuf(emq),给予一个进程使用放输出的数据,Putbuf(outq,hout)
提取输出:Getbuf(outq),给予一个进程输出要输出的数据,Putbuf(emq,sout)
-
磁盘存储器的性能和调度
选择好的磁盘调度算法能减少磁盘的寻道时间,提高磁盘IO速度。
-
磁盘性能简述
-
数据的组织和格式
一个磁盘设备包括一个或多个物理盘片,每个磁盘片分为一个或两个存储面,每个盘面上有若干个磁道,磁道之间有间隙,为了处理方便,每条磁道存储相同数目数据,显然内层数据密度更高。每条磁道有被分为若干个扇区,扇区之间有间隙。
在现代磁盘中,为了提高存储密度,不再使磁道存储数据相同,因此其扇区数也不同,但是向上层隐藏了这些细节方便处理。
为了存储数据,需要现将磁盘低级格式化,如温盘就会格式化如下。

然后要对磁盘进行分区,分区和引导区(某个被标记为活动的分区),存储在磁盘0扇区的主引导记录分区表中的分区表中。
此外,还需要进行一次高级格式化,设置一个引导块,空闲存储管理,根目录和一个空文件系统,并标记使用的文件系统。
-
磁盘的类型
固定头磁盘:每一条磁道上都有一磁头,可并行读写,用于大型。
移动头磁盘:一个盘面一个磁头,只能串行读写,速度慢,但结构简单,用于中小型。
-
磁盘访问时间
寻道时间+旋转延迟时间+传输时间
-
-
早期的磁盘调度算法
- 先来先服务(FCFS):根据请求访问的先后进行调度。
- 最短寻道时间优先(SSTF)(饥饿):先访问与当前磁道距离最近的磁道,使寻道时间最短,但不能保证平均寻道时间最短。
-
基于扫描的磁盘调度算法
-
扫描(SCAN)算法:不仅考虑距离,还考虑方向,每次只找当前移动方向上最近的,如果不再有更大的就调转方向。0→x→0→x…
-
循环扫描(CSCAN)算法
上述问题可能会出现刚刚离开这一道突然出现了一个新的这里的请求。因此循环扫描算法要求向单向移动,到达最大后立刻又从0开始。0→x,0→x…
-
NStepScan算法
如果对某一个磁道有较高的访问频率,那么前3种都会导致磁臂在这里停止不动,而其他可能就会很延迟才能被访问到。NStepScan按照FCFS将所有请求分组,对于每一个组使用SCAN算法。
-
FSCAN算法
是NStepScan的简化版,只有两个队列。一开始有的请求在第一个队列里,开始SCAN扫描,知道这个队列全部完成,而在这期间产生的新的请求放到另一个队列中,在第一个队列处理完后再交换处理这个队列。
-