IO管理

IO 设备

根据信息交换可以分为

  • 块设备 -- 传输速率高、支持随机读取的设备以块单位进行数据交换,如磁盘
  • 字符设备 -- 以字符为单位进行信息交换,通常使用中断方式实现异步通信,比如打印机

打印机需要逐字符对照进行打印

根据传输速率可以分为

  • 低速设备 -- 键盘鼠标等,传输速率低,大致为几字节每秒到几百字节每秒
  • 中速设备 -- 打印机等,大约数百至数千字节每秒
  • 高速设备 -- 磁盘,显卡等

按共享属性分类为

  • 独占设备 -- 同一时刻只允许单一进程使用,直到释放,比如打印机
  • 共享设备 -- 同一时刻单独访问,但是允许进程逻辑上并发,通过调度实现多进程访问,分时交替使用,比如磁盘
  • 虚拟设备 -- 通过SPOOLing 设备将独占设备改造为多个逻辑设备并发,使用高速共享设备作为缓冲区,实现独占设备的逻辑共享。

IO接口

IO 接口(设备控制器)是CPU与IO 设备之间的桥梁。 设备管理器与CPU通过三根逻辑区分的总线相连,分别为数据总线、地址总线、控制总线。

总线端维护了数据寄存器与控制/状态寄存器用于解决总线与IO速度不同步的信息缓冲问题

在现代总线结构,比如显卡的PCIe结构中,都是使用高速串行总线结构,实际为单根高速总线的串行结构,在物理结构上串行,在逻辑结构上分段。

IO接口可以有一个或者多个面向设备的接口,每一个接口可以传输数据、控制、状态信号。

IO接口中存在IO逻辑电路,用于实现对于设备的控制.CPU启动设备后,向设备控制器发送相应命令与地址,但是传输给设备的信息需要经过IO逻辑进行解析并控制发送。

控制设备器的主要作用

  • 接收并识别命令
  • 完成数据交换
  • 标识并报告设备状态
  • 地址识别与数据缓冲 (对应)
  • 差错控制 -- 通过奇偶校验等方式进行差错识别;通过Hamming码等方式进行校验,或者报告给CPU进行信息的重传。

IO 接口的类型

传统意义下IO接口并不是一个大型控制器的集成,而是若干个IO接口功能模块的实现,IO接口根据不同的类型有不同的分类。

根据数据传输的方式分类为并行接口串行接口

根据主机访问方式分为 程序查询接口中断接口DMA接口

根据可编程性分为 可编程接口不可编程接口, 在可编程接口中需要一套执行指令的PC/相关寄存器,甚至单个IO接口就是一个SoC,具有独立指令执行的功能的设备。

根据设备类型不同分为 块设备接口字符设备接口网络设备接口

IO 接口的编址

可以分为独立编址方式与统一编址方式。

独立编址方式中的IO接口的地址空间与主存相对独立,二者不属于相同的地址空间,因此主存的地址值能和主存的某个地址相同。

统一编址方式则是将部分主存地址分配给IO设备,IO设备可以获得较大的编址空间,且可以使用统一的虚拟内存管理方式管理。缺点是减少了内存物理地址可用量。

IO控制方式

分为程序直接控制(程序查询方式)、中断驱动方式与DMA方式。

程序直接控制

程序直接控制方式中,IO设备与主机的数据交换完全由CPU控制,通过程序的执行实现。

实现流程

  • CPU初始化程序并预设相应参数(比如UART传输的波特率、起始地址等)
  • 向IO接口发送命令字并启动外设
  • 循环读取外设状态寄存器值
  • 如果设备就绪就进行数据传送
  • 修改地址与计数器参数
  • 直到计数器归零停止传送

读取外设状态寄存器的查询方式可以分为

  • 独占查询 -- 进程占据CPU进行忙等待查询
  • 定时查询 -- CPU周期性查询,周期间释放CPU资源

程序中断方式

通过中断技术让IO独占总线与CPU一段时间以实现信息与至指令的传输;当IO工作的时候将IO进程中断并实现IO设备和CPU中其他进程的并行工作,以节省CPU的资源。

中断源是能够向CPU发送中断请求的设备或者事件,中断系统为每个中断源设置了中断请求标记触发器,置1时表示中断源请求中断。

通过INTR的信号线发出的是可屏蔽中断。通过NMI的信号线发送的是不可屏蔽中断。不可屏蔽中断优先级高于可屏蔽中断,且可屏蔽中断在关中断状态不能中断。

中断响应的过程可分为

  • 关中断
  • 保存断点 -- 程序计数器PC与程序状态字PSW保存入栈或者专用寄存器
  • 中断服务程序寻址 -- 通过中断类型找到对应的中断服务程序入口,并送入PC等待执行
  • 开中断
  • 执行中断服务程序
  • 关中断
  • 恢复现场和PSW
  • 开中断与中断返回

中断隐指令(关中断-保存断点-中断服务寻址)是中断前的准备指令,将前一进程状态保存,并加载中断服务程序以便实现中断。

中断服务程序的实现过程分为中断过程和恢复过程,中断过程(开中断-执行中断服务程序-关中断)进行中断,恢复过程(恢复现场和PSW-开中断与中断返回)将中断现场恢复并开中断

多重中断与中断屏蔽

当系统存在多中断源竞争的时候需要使用优先级机制匹配中断,只有在开中断阶段的时候可屏蔽中断才能参与多重中断的竞争。

多重中断是指多个中断源发生时,低优先级中断过程被高等级中断抢占并优先中断的过程。通过栈结构管理相应多重中断的断点,因为优先级执行机制是FILO的。

中断处理优先级是多重中断的实际处理顺序,可以通过动态屏蔽技术动态调整。分为处理优先级响应优先级,当中断系统不使用动态屏蔽技术的时候处理优先级等于响应优先级。

中断请求寄存器维护每一个中断源的中断请求,中断屏蔽字寄存器维护进程的处理优先级。中断请求经由请求寄存器发送,在屏蔽字寄存器和处理优先级电路的运算下输出在当前中断请求状态下的处理优先级,再通过中断判优电路处理后输出真正的执行中断的响应优先级。

逻辑上的中断实现需要维护一个 n×nn\times n 的中断屏蔽字表,比如

[1111010001100111]\begin{bmatrix} 1&1&1&1\\ 0&1&0&0\\ 0&1&1&0\\ 0&1&1&1 \end{bmatrix}

表示A>D>C>BA>D>C>B,这是静态的中断响应优先级表

但是基于当前中断请求状态的动态执行是通过vector+ 中断请求寄存器更新实现的。

比如在初始请求寄存器为[A,B,C,D]=[0,1,1,1][A,B,C,D] = [0,1,1,1]

  • 第一轮 -- 中断D,请求寄存器更新为[0,1,1,0][0,1,1,0]
  • 第二轮 -- 中断C, 请求寄存器更新为[0,1,0,0][0,1,0,0]
  • 第三轮 -- 中断B, 请求寄存器更新为[0,0,0,0][0,0,0,0], 中断结束

DMA 方式

DMA 在IO接口和内存之间建立轮直接的数据通路,以提高CPU资源利用率,实现数据准准备阶段传输过程中的CPU任务与IO设备的并行。

DMA控制器组成
  • 主存地址计数器 -- 用于存放待传输数据的主存地址,传输一个字就自加确认下一个地址
  • 传送长度计数器 -- 用于标记待传送数据的总长,传送一个字就自减,计数器归零说明传输结束
  • 数据缓冲寄存器 -- 暂存每次传输的数据
  • DMA请求触发器
  • 控制状态逻辑
  • 中断机构 -- DMA中断源

DMA控制器需要竞争总线的能力,且对于总线/CPU的优先级满足

DMA > 不可屏蔽中断 > 可屏蔽中断
DMA 传送方式

DMA 与 CPU 共享主存的方式可分为

  • 停止CPU访问主存 -- DMA直接竞争主存,CPU停止主存相关的访问,直到DMA结束数据传送
  • 周期挪用 -- 在单个访存周期内,DMA优先级高于CPU,但是周期内DMA无法中断CPU访存
  • DMA与CPU交替访存 -- 交替分配主存访存周期
DMA传送过程

DMA传送需要CPU进行预处理,数据传送与后处理三个过程。

  • 预处理 -- 完成DMA的初始化并启动IO设备,当IO设备准备好输入/输出请求时,向DMA发送DMA请求
  • 数据传输 -- DMA直接通过总线访问主存,CPU执行其他进程
  • 后处理 -- CPU响应后执行中断服务程序,并进行数据校验等后处理工作。

IO 软件

IO软件是运行在CPU侧进行IO设备与IO接口管理的软件系统,向上与文件系统、用户进程、虚拟存储器等系统交互,向下与底层IO设备系统交互。

比如NVIDIA的驱动程序,面向下层显卡的实际计算管理,面向上层操作系统与用户暴露API,使用户能通过这些API进行显卡计算能力的使用,比如CUDA/OpenGL等计算

IO 软件根据 用户侧 -- 硬件侧的自下而上的软件分类可分为

用户 - 用户层软件 - 设备独立性软件 - 设备驱动软件 - 中断处理软件 - 硬件

中断处理软件

用于管理IO设备结束等情况下的中断管理,实现最基本的CPU/总线与IO设备的通信层任务

操作系统对于中断处理软件本身也存在一层管理,包括三个环节

  • 注册中断 -- 向内核注册中断号,建立当前设备中断状态与内核记录的服务状态的记录映射,建立CPU和设备间中断状态的握手,确保内核能进行中断处理
  • 处理中断
  • 注销中断 -- 驱动/设备停用的时候恢复中断现场,注销中断处理程序并释放相应资源。

设备驱动程序

面向不同种类设备的专用管理软件,比如打印机的打印机驱动,鼠标设备的驱动,NVIDIA显卡的显卡驱动等。不同设备向主机暴露的API不同,访问方式不同,设备驱动程序正是提供这些API的软件层。

类比VFS的统一管理多协议磁盘的形式,设备驱动程序类似于磁盘文件系统NTFS/ext4

设备独立层软件

操作系统将设备驱动程序进行统一管理,比如网络的统一化接口Socket层,进行用户层网络调用的编程不需要考虑通过有线还是通过无线进行网络访问,而直接通过操作系统暴露的Socket获得网络资源。

设备独立层软件引入物理设备与逻辑设备的概念,将实际用户层的访问与物理设备相解耦。比如在Unix中访问网卡信息,通常获得的不是真实的网卡名,而是映射后的网卡号以及相关信息

asuna@AsunadeMacBook-Air ~ % ifconfig
lo0: ...
gif0: ...
stf0: ...
anpi0: ...
anpi1: ...
en3: ...
en4: ...
en1: ...
en2: ...
ap1: ...
en0: ...
utun0: ...
awdl0: ...
llw0: ...
utun1:...
utun2: ...
utun3: ...

其中en*是映射真实物理网卡的逻辑网卡接口
lo0是本地回环的网络接口,纯粹软件虚拟实现且指向127.0.0.1
anpi*是Apple Silicon 的内部协议与通信接口,用于支撑内部系统服务的部分通信,并将这样的总线通信权限通过anpi*暴露给用户。

用户层软件

用户层软件就是整体实现与用户交互的软件接口,通过调用用户层的IO库函数与硬件进行交互。

应用程序IO接口

应用程序IO接口是OS面向用户侧提供的管理与访问IO设备的架构与编程规范。

OS将物理设备抽象为块设备字符设备网络设备,网络设备虽然速度匹配,但是网络通信使用的是不可随机访问的数据包而不是数据块,因此不算块设备的范畴。

  • 字符设备接口 -- get/put操作用于向缓冲区读写;in-control 用于实现具体设备的控制; open/close用于打开关闭与互斥访问

  • 块设备接口 -- 块设备通常为存储设备,因此块设备接口通常暴露的就是存储相关能力的接口。read/write/seek用于执行基本功能,seek用于指定下一个传输块。

阻塞IO与非阻塞IO

IO接口面向不同的设备有两种执行模式: 阻塞与非阻塞,

阻塞IO能在进程发起IO请求但是资源未满足时发生阻塞,并移动到相应等待队列。

非阻塞IO发生时,进程会轮询资源就绪情况,且进程能同时执行其他的任务而不受IO请求的影响。

设备独立性软件

设备独立性软件和VFS相似,在设备驱动程序层之上,实现不同设备等公共操作,并暴露统一且抽象化的API。具体功能有

  • 统一的驱动程序接口
  • 缓冲管理 -- 缓冲CPU和IO设备的速度差异,建立统一的缓冲区对应硬件层的软件实现。
  • 差错控制
  • 独占设备的分配与回收
  • 统一的逻辑数据块

高速缓存与缓冲区

磁盘高速缓存(Disk cache)基于内存实现,利用内存的存储空间暂存磁盘读取的盘块信息以实现加快磁盘IO速度的效果。在内存实现方式有两种

  • 在内存中开辟固定大小的专用缓冲区
  • 将内存中空闲的空间作为动态缓冲池,供分页系统与磁盘IO子系统使用。

缓冲区

IO设备大部分情况下IO速度远远慢于CPU的速度(如打印机),也可能远快于CPU(显卡)

缓冲区的实现方式有

  • 硬件缓冲区 -- 实现成本较高,关键部位才使用
  • 内存缓冲区 -- 常见实现方式

缓冲区总是单个时刻可读/可写的,所以其能实现的作用是在CPU处理时间内作为数据蓄水池

单缓冲

在缓冲区结构中,单次IO时间分为三个部分

  • IO设备写入缓冲区的时间 TT
  • 缓冲区传送给CPU的时间 MM
  • CPU处理时间 CC

在单个周期内,当MiM_i结束,就能执行Ti+1T_{i+1}.如果Ci>Ti+1C_{i}> T_{i+1} ,即CPU处理时间长于IO写时间,则缓存区写等待CPU执行;反之CPU写等缓存区写。通常情况下都是 Ci<Ti+1C_{i}<T_{i+1}, 因此单轮耗时为

Ts=max(C,T)+M\mathcal{T}_s = \max(C,T)+M

对于NN轮IO写入的作业,总耗时为

Ttotal=(N1)(max(C,T)+M)+(T+C+M)\mathcal{T}_{total} = (N-1)(\max(C,T)+M)+(T+C+M)

最后一轮没有CCTT在时间轴上的cover,因此都需要计算

双缓冲

双缓冲是可切换的两个缓冲区的结构。在缓存区1写的时候,缓存区2也能向CPU传输信息

如果IO写时间 TT > 从缓冲区传入CPU的总时间 M+CM+C, 则缓冲区2空闲等待缓冲区1写,反之需要缓冲区1满等待缓冲区2执行。因此单轮执行时间为

Ts=max(T,M+C)\mathcal{T}_s = \max(T,M+C)

N轮的总执行时间为

Ttotal=(N1)max(T,M+C)+(T+M+C)\mathcal{T}_{total} = (N-1)\max(T,M+C) +(T+M+C)

双缓冲能实现全双工通信,双向传输信息,每一台设备设置发送缓冲区与接收缓冲区

循环缓冲

循环缓冲区的实现逻辑与循环链表类似,通过环形空间和in/out两个指针指向当前写入的缓冲区与传入CPU的缓冲区。

如果in指针追上out指针则为CPU速度慢于IO写速度;反之则为IO写速度慢于CPU速度

缓冲池

缓冲池是一套包括管理数据结构和操作函数的,能管理多个缓冲区的软件机制,能支持多进程共享。

缓冲池按四种方式进行工作

  • 收容输入 -- 将输入数据存入输入缓冲区
  • 提取输入 -- 将输入缓冲区信息传入CPU并清空输入缓冲区
  • 收容输出 -- 从CPU将输出数据输入输出缓冲区
  • 提取输出 -- 将输出缓冲区的数据输出到IO并清空输出缓冲区

设备分配与回收

设备独立性软件维护了一张系统设备表(SDT),包含所有物理设备与相应的全局索引。每一个表项对应一个设备,包含设备类型、标识符等用于检索设备的信息。

每一个物理设备私有一个设备控制表(DCT), 管理设备的各项具体属性,比如设备类型、设备标识符、设备状态、指向设备器控制表的指针、重复执行次数与时间和设备的队首指针等。

**设备器控制表(COCT)用于标记设备器的状态信息,存在指向通道控制表(CHCT)**的指针

通道控制表(CHCT) 标记每一个通道的状态信息,以及通道相关的所有的控制器的信息。

通道是内存和IO设备的通信通道,是一个协处理器级别的IO管理设备,用于实现更加复杂化,可编程的,类似于DMA的IO设备管理。

根据访问顺序自上而下的排列为

SDT -> DCT -> COCT -> CHCT

设备的分配的顺序为

  • 设备分配 -- 查询SDT并找出对应设备的DCT,分配设备
  • 控制器分配 -- 根据COCT找到对应设备的控制器
  • 通道分配 -- 控制器分配后根据COCT找到对应CHCT, 分配内存和IO设备间的通道。

设备的分配算法会考虑是否死锁,

  • 安全分配方式 -- 死锁避免,进程IO请求后进入阻塞态
  • 不安全分配方式 -- 只有当请求设备被其他进程占用时才进入阻塞态。但是可能由于资源竞争造成死锁

死锁竞争的讨论见 进程的同步与互斥死锁

逻辑设备与物理设备的映射

设备的逻辑化需要建立逻辑设备和物理设备之间的映射,对应的映射表为逻辑设备表(LUT)

逻辑设备表可以为整个系统维护一个LUT表或者为单个用户维护LUT表,前者适用于单用户系统,后者适用于多用户系统

SPOOLing技术

SPOOLing技术用于缓和高速CPU和低速IO之间的速度矛盾。其核心是将高速磁盘作为缓冲进行相应信息的临时存储,通过软件模拟脱机IO的过程。

脱机IO的本质是将不同的作业拿到外围机进行并行IO作业,一方面解放主计算机的CPU,一方面可以保证IO作业的连续性。

通过SPOOLing技术能通过多道程序和磁盘缓冲空间代替IO的外围机,从而将一台物理独占设备仿真为多个逻辑设备。具体场景为多用户共享的打印中心。

SPOOLing技术的构成结构有

  • 输入井和输出井 -- 位于磁盘中的缓冲区,用于暂存输入/输出设备的文件(称为井文件)
  • 输入/输出缓冲区 -- 在内存中用于暂存输入/输出设备的数据,这是磁盘写速度慢于内存的平衡buffer
  • 输入输出进程
  • 井管理系统 -- 控制作业和磁盘井的信息交换

磁盘

磁盘与SSD