目录
I/O 层次结构分为五层:
- 用户层 I/O 软件
- 设备独立性软件
- 设备驱动程序
- 中断处理程序
- 硬件
其中,设备独立性软件、设备驱动程序、中断处理程序属于操作系统的内核部分,即“I/O 系统”,或称“I/O 核心子系统”。
1 用户层 I/O 软件
用户层软件实现了与用户交互的接口,用户可直接使用该层提供的、与 I/O 操作相关的库函数对设备进行操作。
Windows 操作系统向外提供的一系列系统调用,但是由于系统调用的格式严格,使用麻烦,因此在用户层上封装了一系列更方便的库函数接口供用户使用(Windows API)。
1.1 假脱机技术(SPOOLing 技术)
“假脱机技术”,又称“SPOOLing 技术”是用软件的方式模拟脱机技术。要实现 SPOOLing 技术,必须要有多道程序技术的支持。系统会建立 “输入进程”和 “输出进程”。SPOOLing 技术可以把一台物理设备虚拟成逻辑上的多台设备,可将独占式设备改造成共享设备,每个用户进程都觉得自己在独占一台设备,从而实现对设备的共享,也缓解了 CPU 和设备的速度矛盾。
【注】408 大纲又将假脱机技术归为“ I/O 核心子系统” 的功能, 因此考试时还是以大纲为准。
1.1.1 SPOOLing 系统的组成
SPOOLing 技术系统由预输入程序、井管理程序(本质上可以理解成目录)和缓输出程序组成。预输入程序、井管理程序和缓输出程序合起来也叫守护进程。
- 输入井:模拟脱机输入时的磁带,用于收容 I/O 设备输入的数据,是在磁盘上开辟出的存储区域。
- 输出井:模拟脱机输出时的磁带,用于收容用户进程输出的数据,是在磁盘上开辟出的存储区域。
- 输入缓冲区:位于内存,在输入进程的控制下,“输入缓冲区”用于暂存从输入设备输入的数据,之后再转存到输入井中。
- 输出缓冲区:位于内存,在输出进程的控制下,“输出缓冲区”用于暂存从输出井送来的数据,之后再传送到输出设备上。
1.1.2 假脱机管理进程的工作原理
用户进程提出输出打印的请求时,由假脱机管理进程为每个进程做两件事:
- 在磁盘输出井中为进程申请一个空闲缓冲区,并将要打印的数据送入其中;
- 为用户进程申请一张空白的打印请求表,并将用户的打印请求填入表中,再将该表挂到假脱机文件队列上。
打印机空闲时,输出进程会从文件队列的队头取出一张打印请求表,并根据表中的要求将要打印的数据从输出井传送到输出缓冲区,再输出到打印机进行打印。
1.2 应用程序接口
输入/输出应用程序接口位于用户层和设备独立层之间。
1.2.1 字符设备接口
- get/put 系统调用:向字符设备缓冲区读/写一个字符。
1.2.2 块设备接口
- read/write 系统调用:向块设备的读/写指针位置读/写多个字符。
- seek 系统调用:修改读/写指针位置。
1.2.3 网络设备(网络套接字)接口
- socket 系统调用:创建一个网络套接字,需指明网络协议。
- bind 系统调用:将套接字绑定在某个本地端口。
- connect 系统调用:将套接字连接到远程地址。
- read/write 系统调用:从套接字读/写数据。
1.2.4 阻塞/非阻塞 I/O
- 阻塞 I/O:应用程序发出 I/O 系统调用,进程需转为阻塞态等待。例如,字符设备接口的 get 调用(从键盘读一个字符);C 语言中的 scanf 函数。
- 非阻塞 I/O:应用程序发出 I/O 系统调用,系统调用可迅速返回,进程无需阻塞等待。例如,块设备接口的 write 调用(往磁盘写数据);C 语言中的 printf 函数。
2 设备独立性软件
设备独立性软件,又称设备无关性软件。与设备的硬件特性无关的功能几乎都在这一层实现。
2.1 I/O 调度
I/O 调度指的是用某种算法确定一个好的顺序来处理各个 I/O 请求。
对于磁盘,有这些调度算法:先来先服务算法、最短寻道优先算法、SCAN 算法、C-SCAN 算法、LOOK 算法、C-LOOK 算法(见《【组成原理-存储】存储器的相关知识》)。当多个磁盘 I/O 请求到来时,用其中一种调度算法确定满足 I/O 请求的顺序。
同理,打印机等设备也可以用先来先服务算法、优先级算法、短作业优先等算法来确定 I/O 调度顺序。
2.2 设备保护
在 UNIX 系统中,设备被看做是一种特殊的文件,每个设备也会有对应的 FCB。当用户请求访问某个设备时,系统根据 FCB 中记录的信息来判断该用户是否有相应的访问权限,以此实现“设备保护”的功能。
2.3 设备的分配与回收
2.3.1 设备分配的方式
从进程运行的安全性上考虑:
方式 | 描述 | 优点 | 缺点 |
---|---|---|---|
安全分配方式 | 为进程分配一个设备后就将进程阻塞,本次 I/O 完成后才将进程唤醒 | 破坏了“请求和保持”条件,不会死锁 | 对于一个进程来说,CPU 和 I/O 设备只能串行工作 |
不安全分配方式 | 进程发出 I/O 请求后,系统为其分配 I/O 设备,进程可继续执行,之后还可以发出新的 I/O 请求,只有某个 I/O 请求得不到满足时才将进程阻塞 | 进程的计算任务和 I/O 任务可以并行处理,使进程迅速推进 | 有可能发生死锁 |
从获取资源的时机上考虑:
- 静态分配:进程运行前为其分配全部所需资源,运行结束后归还资源(破坏了“请求和保持”条件,不会发生死锁)。
- 动态分配:进程运行过程中动态申请设备资源。
2.3.2 设备分配的数据结构
数据结构 | 描述 |
---|---|
系统设备表(SDT) | 记录了系统中全部设备的情况,每个设备对应一个表目。每个表目存有 DCT(设备控制表)的指针,以及设备类型(即逻辑设备名) |
设备控制表(DCT) | 系统为每个设备配置一张 DCT,用于记录设备情况。每个表目存有 COCT(控制器控制表)的指针 |
控制器控制表(COCT) | 每个设备控制器(即 I/O 接口)都会对应一张 COCT,操作系统根据 COCT 的信息对控制器进行操作和管理。每个表目存有 CHCT(通道控制表)的指针 |
通道控制表(CHCT) | 每个通道都会对应一张 CHCT,操作系统根据 CHCT 的信息对通道进行操作和管理 |
【注】一个通道控制多个控制器,一个控制器控制多个设备。
物理设备和逻辑设备的映射:
- 物理设备名(绝对号):进程请求分配设备时提供的参数。
- 逻辑设备名:用户编程时提供的逻辑设备名其实就是“设备类型”。建立逻辑设备名与物理设备名的映射机制,用户编程时只需提供逻辑设备名。
- 逻辑设备表(LUT):建立了逻辑设备名与物理设备名之间的映射关系。每个表目记录了逻辑设备名对应的物理设备名和驱动程序入口地址。
【注】若整个系统只有一张 LUT,则各用户所用的逻辑设备名不允许重复,适用于单用户操作系统;若每个用户一张 LUT,则不同用户的逻辑设备名可重复,适用于多用户操作系统。
2.3.3 设备分配的流程
- 根据进程请求的逻辑设备名查找 SDT,找到用户进程指定类型的、并且空闲的设备,将其分配给该进程,然后操作系统在逻辑设备表(LUT)中新增一个表项。
- 根据 SDT 找到 DCT,若设备忙碌则将进程 PCB 挂到设备等待队列中,不忙碌则将设备分配给进程。
- 根据 DCT 找到 COCT,若控制器忙碌则将进程 PCB 挂到控制器等待队列中,不忙碌则将控制器分配给进程。
- 根据 COCT 找到 CHCT,若通道忙碌则将进程 PCB 挂到通道等待队列中,不忙碌则将通道分配给进程。
- 只有设备、控制器、通道三者都分配成功时,这次设备分配才算成功,之后便可启动 I/O 设备进行数据传送。
【注】如果之后用户进程再次通过相同的逻辑设备名请求使用设备,则操作系统通过 LUT 即可知道用户进程实际要使用的是哪个物理设备了,并且也能知道该设备的驱动程序入口地址。
2.4 缓冲区管理
默认:一个缓冲区的大小就是一个块。
从缓冲区读入数据的过程如下:
- 在块设备输入数据的过程中,首先把磁盘数据送到缓冲区,花费的时间为 T,简记为 T 过程。
- 然后把操作系统缓冲区的数据送到用户区,花费的时间为 M,简记为 M 过程。
- 最后用户进程对这批数据进行计算,花费的时间为 C,简记为 C 过程。
【注】当缓冲区数据非空时,不能往缓冲区冲入数据,只能从缓冲区把数据传出;当缓冲区为空时,可以往缓冲区冲入数据,但必须把缓冲区充满以后,才能从缓冲区把数据传出。
2.4.1 单缓冲
T 过程和 M 过程不能并行,C 过程和 M 过程不能并行。
大致过程:
. | . | . | . | . | . | . |
---|---|---|---|---|---|---|
T | M | C | . | . | . | . |
. | . | T | M | C | . | . |
. | . | . | T | M | C | . |
现具体分析如下:
(1)T > C
(假设缓冲次数为 n 次,T = T1 + T2,最底下一行为总结)
. | . | . | . | . | . | . | . | . | . |
---|---|---|---|---|---|---|---|---|---|
T1 | T2 | M | C | . | . | . | . | . | . |
. | . | . | T1 | T2 | M | C | . | . | . |
. | . | . | . | . | . | T1 | T2 | M | C |
T1 | T2 | M | T1 | T2 | M | T1 | T2 | M | C |
- 总时间 = n * (T + M) + C
- 平均时间 = (n * (T + M) + C) / n = T + M (n 足够大)
(2)T < C
(假设缓冲次数为 n 次,C = C1 + C2,最底下一行为总结)
. | . | . | . | . | . | . | . | . | . |
---|---|---|---|---|---|---|---|---|---|
T | M | C1 | C2 | . | . | . | . | . | . |
. | . | T | . | M | C1 | C2 | . | . | . |
. | . | . | . | . | T | . | M | C1 | C2 |
T | M | C1 | C2 | M | C1 | C2 | M | C1 | C2 |
- 总时间 = T + n * (M + C)
- 平均时间 = (T + n * (M + C)) / n = M + C (n 足够大)
综上,单缓冲的平均时间 = max { T, C } + M。
2.4.2 双缓冲
T 过程和 M 过程可以并行,C 过程和 M 过程不能并行。
大致过程:
. | . | . | . | . | . | . |
---|---|---|---|---|---|---|
T | M | C | . | . | . | . |
. | T | M | C | . | . | . |
. | . | T | M | C | . | . |
现具体分析如下:
(1)T > C
(假设缓冲次数为 n 次,T = T1 + T2,最底下一行为总结)
. | . | . | . | . | . | . | . | . | . |
---|---|---|---|---|---|---|---|---|---|
T1 | T2 | M | C | . | . | . | . | . | . |
. | . | T1 | T2 | M | C | . | . | . | . |
. | . | . | . | T1 | T2 | M | C | . | . |
T1 | T2 | M | C | M | C | M | C | . | . |
- 总时间 = T + n * (M + C)
- 平均时间 = (T + n * (M + C)) / n = M + C (n 足够大)
(2)T > C,M << C,M << T
(假设缓冲次数为 n 次,T = T1 + T2,M 很小,可以忽略不计,因而可直接算入 T2 中。最底下一行为总结)
. | . | . | . | . | . | . | . | . | . |
---|---|---|---|---|---|---|---|---|---|
T1 | T2 (+ M) | C | . | . | . | . | . | . | . |
. | T1 | T2 (+ M) | C | . | . | . | . | . | . |
. | . | T1 | T2 (+ M) | C | M | . | . | . | . |
T1 | T2 | T1 | T2 | C | M | . | . | . | . |
- 总时间 = T * n + C + M
- 平均时间 = (T * n + C + M) / n = T (n 足够大)
(3)T < C
(假设缓冲次数为 n 次,C = C1 + C2,最底下一行为总结)
. | . | . | . | . | . | . | . | . | . |
---|---|---|---|---|---|---|---|---|---|
T | M | C1 | C2 | . | . | . | . | . | . |
. | T | . | . | M | C1 | C2 | . | . | . |
. | . | . | . | T | . | . | M | C1 | C2 |
T | M | C1 | C2 | M | C1 | C2 | M | C1 | C2 |
- 总时间 = T + n * (M + C)
- 平均时间 = (T + n * (M + C)) / n = M + C (n 足够大)
综上,双缓冲的平均时间 = max { T, C } + M;如果 M << C,M << T,则平均时间 = max { T, C }。因此结论又可以整合为 max { T, C+M }。
2.4.3 循环缓冲
将多个大小相等的缓冲区链接成一个循环队列。
- in 指针:指向下一个可以冲入数据的空缓冲区。
- out 指针:指向下一个可以取出数据的满缓冲区。
2.4.4 缓冲池
缓冲池由系统中共用的缓冲区组成。这些缓冲区按使用状况可以分为:空缓冲队列、装满输入数据的缓冲队列(输入队列)、装满输出数据的缓冲队列(输出队列)。
设置四种工作缓冲区:
- 用于收容输入数据的工作缓冲区(hin)
- 用于提取输入数据的工作缓冲区(sin)
- 用于收容输出数据的工作缓冲区(hout)
- 用于提取输出数据的工作缓冲区(sout)
缓冲池的工作原理:
- 进程请求输入数据:从空缓冲队列中取出一块作为收容输入数据的工作缓冲区(hin)。冲满数据后将缓冲区挂到输入队列队尾。
- 进程取得一块输入数据:从输入队列中取得一块冲满输入数据的缓冲区作为“提取输入数据的工作缓冲区(sin)”。缓冲区读空后挂到空缓冲区队列。
- 进程将准备好的数据冲入缓冲区:从空缓冲队列中取出一块作为“收容输出数据的工作缓冲区(hout)”。数据冲满后将缓冲区挂到输出队列队尾。
- 进程请求输出数据:从输出队列中取得一块冲满输出数据的缓冲区作为“提取输出数据的工作缓冲区(sout)”。缓冲区读空后挂到空缓冲区队列。
2.4.5 相关例题
【例 1】设从磁盘将一块数据传送到缓冲区所用时间为 80us,将缓冲区中数据传送到用户区所用时间为 40us,CPU 处理一块数据所用时间为 30us。如果有多块数据需要处理,并采用单缓冲区传送某磁盘数据,则处理一块数据所用总时间为?
【解】T = 80us,M = 40us,C = 30us。
有多块数据需要处理,则处理一块数据的平均时间 = max { T, C } + M = 80us + 40us = 120us。
【例 2】某操作系统采用双缓冲传送磁盘上的数据。设从磁盘将数据传送到缓冲区所用时间为 T1,将缓冲区中数据传送到用户区所用时间为 T2(假设T2 << T1),CPU 处理数据所用时间为 T3,则处理该数据,系统所用总时间为?
【解】直接使用结论:max { T, C } + M,如果 M << C,M << T,则平均时间 = max { T, C } = max { T1, T3 }。
【例 3】某文件占 10 个磁盘块,现要把该文件的磁盘块逐个读入主存缓冲区,并送用户区进行分析。一个缓冲区与磁盘块大小相等。把一个磁盘块读入缓冲区的时间为 100μs,缓冲区数据传送到用户区的时间是 50μs,CPU 对一块数据进行分析的时间为 50μs。分别计算在单缓冲区和双缓冲区结构下,分析完该文件的时间是多少?
【解】T = 100us,M = 50us,C = 50us。注意到 T > C。
单缓冲总时间 = n * (T + M) + C = 10 * (100 + 50) + 50 = 1550us。
双缓冲总时间 = T + n * (M + C) = 100 + 10 * (50 + 50) = 1100us。
【例 4】设系统缓冲区和用户工作区均采用单缓冲,从外设读入 1 个数据块到系统缓冲区的时间为 100,从系统缓冲区读入 1 个数据块到用户工作区的时间为 5, 对用户工作区中的 1 个数据块进行分析的时间为 90。进程从外设读入并分析 2 个数据块的最短时间是?
【解】T = 100,M = 5,C = 90。注意到 T > C。
单缓冲总时间 = n * (T + M) + C = 2 * (100 + 5) + 90 = 300。
【注】这些公式不用背,实际做题时只需像上面那样画出三段的图,即可推得公式。
3 设备驱动程序
不同的 I/O 设备有不同的硬件特性,具体细节只有设备的厂家才知道。因此厂家需要根据设备的硬件特性设计并提供相应的驱动程序。
设备驱动程序主要负责对硬件设备的具体控制,将上层发出的一系列命令(如 read/write、get 等)转化成特定设备“能听得懂”的一系列操作。包括设置设备寄存器、检查设备状态等。驱动程序一般会以一个独立进程的方式存在。
若各公司开发的驱动设备程序接口不统一,则操作系统很难调用设备驱动程序,因此操作系统规定好设备驱动程序的接口标准,各厂商必须按规定要求开发设备驱动程序。
与此同时,不同的操作系统也对设备驱动程序接口的标准各不相同。设备厂商必须根据操作系统的接口要求,开发相应的设备驱动程序,设备才能被使用。
4 中断处理程序
当 I/O 任务完成时,I/O 控制器会发送一个中断信号,系统会根据中断信号类型找到相应的中断处理程序并执行。中断处理程序的处理流程如下:
- 从控制器读出设备状态。
- 检测 I/O 是否正常结束,若否,根据异常原因做相应处理,若是,继续下一步。
- 从设备中读入一个字的数据并经由 CPU 放到内存缓冲区中。
5 硬件
5.1 I/O 设备
按设备特性分类:
- 独占式使用设备:一个时段只能分配给一个进程(如打印机)。
- 分时式共享使用设备:可同时分配给多个进程使用(如磁盘),各进程往往是宏观上同时共享使用设备,实际在微观上是交替使用。
- 虚拟设备:采用 SPOOLing 技术将独占设备改造成虚拟的共享设备,可同时分配给多个进程使用(如采用 SPOOLing 技术实现的共享打印机)。
按信息交换的单位分类:
- 块设备:如磁盘、SSD,属于共享设备。数据传输的基本单位是“块”,传输速率较高,可寻址,即对它可随机地读/写任一块。
- 字符设备:如键盘、打印机,属于独占设备。数据传输的基本单位是字符。传输速率较慢,不可寻址,在输入/输出时常采用中断驱动方式。
5.2 I/O 接口(I/O 控制器、设备控制器)
I/O 接口又称为 I/O 控制器或设备控制器,由内部接口、外部接口、I/O 逻辑、I/O 端口组成。
I/O 接口的工作原理:
- 发命令:发送命令字到控制寄存器,向设备发送命令(需要驱动程序的协助)。
- 读状态:从状态寄存器读取状态字,获得设备或 I/O 控制器的状态信息。
- 读/写数据:从数据缓冲寄存器发送或读取数据,完成主机与外设的数据交换。
I/O 接口的作用:
- 数据缓冲:通过数据缓冲寄存器(DBR)达到主机和外设工作速度的匹配。
- 错误或状态监测:通过状态寄存器反馈设备的各种错误、状态信息,供 CPU 查用。
- 控制和定时:接收从控制总线发来的控制信号、时钟信号。
- 数据格式转换:串-并、并-串等格式转换。
- 与主机和设备通信:实现 主机—I/O接口—I/O设备 之间的通信。
5.2.1 内部接口
即 CPU 与控制器的接口,用于实现 CPU 与控制器之间的通信。CPU 通过控制线发出命令;通过地址线指明要操作的设备;CPU 通过数据线来取出数据,或放入数据。内部接口与系统总线(数据线、地址线、控制线)相连,实质上是与内存、CPU 相连,其中数据线和 I/O 端口相连。
5.2.2 外部接口
即控制器与设备的接口,用于实现控制器与设备之间的通信。外部接口通过接口电缆与外设相连,外部接口的数据传输可能是串行方式,因此 I/O 接口需具有串/并转换功能。
5.2.3 I/O 逻辑
I/O 逻辑负责接收和识别 CPU 的各种命令(如地址译码),并负责对设备发出命令。
5.2.4 I/O 端口
I/O 端口指设备控制器中可被 CPU 直接访问的寄存器,包括:
- 数据(缓冲)寄存器:实现 CPU 与外设之间的数据缓冲。
- 状态寄存器:获取执行结果和设备的状态信息。
- 控制寄存器:由 CPU 写入,以便启动或更改设备模工作式。
【注 1】控制寄存器、状态寄存器在使用时间上是错开的,因此有的 I/O 接口中可将二者合为控制/状态寄存器。
【注 2】数据寄存器、控制寄存器、状态寄存器可能有多个(如:每个控制/状态寄存器对应一个具体的设备)。
I/O 端口的编址方式有两种:
方式 | 描述 | 优点 | 缺点 |
---|---|---|---|
独立编址(寄存器独立编址) | I/O 端口地址和主存地址空间统一,用访存指令访问 I/O 端口(RISC 机器常用,如 STM32) | 不需要专门的输入/输出指令,端口有较大的编址空间,程序设计灵活性高 | 端口占用了主存地址空间,使主存地址空间变小,外设寻址时间长 |
统一编址(内存映像 I/O) | I/O 端口地址和主存地址相互独立,用 I/O 指令访问 I/O 端口(如 80386 的 IN、OUT 指令) | 使用专用 I/O 指令,程序编制清晰,I/O 端口地址位数少,地址译码速度快,I/O 端口的地址不占用主存地址空间 | I/O 指令类型,程序设计灵活性差,增加了 CPU 的控制逻辑电路的复杂度 |
【注 1】统一编址方式下,I/O 端口地址要求固定在地址空间的某部分,不能随意在地址空间的任意位置。
【注 2】端口是指接口电路中可以进行读/写的寄存器,若干端口加上相应的控制逻辑才可以组成接口。