统一编址(内存映射 I/O)
控制器寄存器占用内存地址空间的一部分,和内存单元一起编址。
- 用普通访存指令(如
mov)就能读写寄存器,不需要专门的 I/O 指令; - 代价:占用一部分内存地址空间;访存指令和 I/O 操作看起来一样,要靠地址区分。
这一章的主线只有一条:CPU 太快,设备太慢,还五花八门。硬件上让 CPU 越管越少(中断、DMA、通道),软件上分层屏蔽差异,再用缓冲和假脱机把速度差垫平。最后单讲最重要的块设备:磁盘,以及怎么排磁道请求。
设备的机械部分千差万别,电子部分(设备控制器 / I/O 接口)把它包装成几个寄存器。CPU 往控制寄存器写命令,从状态寄存器读状态,通过数据寄存器交换数据。
| 分类依据 | 类别 | 特点 | 例子 |
|---|---|---|---|
| 信息交换单位 | 块设备 | 以数据块为单位传输;传输快,可寻址,可随机读写任一块;常用 DMA | 磁盘、SSD |
| 字符设备 | 以字符为单位;传输慢,不可寻址;常用中断驱动 | 键盘、鼠标、打印机 | |
| 传输速率 | 低速 / 中速 / 高速 | 每秒几字节到几百字节 / 数千到数万字节 / 数百 KB 到数 MB 以上 | 键盘鼠标 / 行式打印机 / 磁盘光盘 |
| 使用特性(用途) | 人机交互 / 存储 / 网络通信 | 交互类慢;存储类快;通信类中等 | 显示器键盘 / 移动硬盘 / 网卡、调制解调器 |
| 共享属性 | 独占 / 共享 / 虚拟 | 一段时间只给一个进程 / 宏观上多个进程同时用、微观上交替 / 用 SPOOLing 把独占设备虚拟成多台 | 打印机 / 磁盘 / 虚拟打印机 |
控制器寄存器占用内存地址空间的一部分,和内存单元一起编址。
mov)就能读写寄存器,不需要专门的 I/O 指令;寄存器(I/O 端口)有一套独立的地址,叫端口号,和内存地址可以重叠。
IN、OUT)访问;四种方式比的都是同一件事:一次 I/O 里 CPU 要介入几次、数据要不要经过 CPU。
CPU 向控制器发读命令; /* 设备没就绪就一直查,CPU 空转 */ while (状态寄存器 != 就绪) ; R = 数据寄存器; /* 设备 → CPU 寄存器 */ mem[i++] = R; /* CPU 寄存器 → 内存 */ /* 每读一个字都重复上面这些 */
实现简单。代价:CPU 与 I/O 完全串行,CPU 利用率极低。
CPU 发出读命令后转去执行别的进程;设备把一个字准备好放进数据寄存器,就发中断。CPU 在每条指令周期末尾检查中断,响应后由中断处理程序把这个字读进 CPU 寄存器,再写入内存。
CPU 与 I/O 可以并行了。代价:每个字(字节)都要中断一次,保存和恢复现场开销大;数据仍然要经过 CPU。
CPU 只在开始时把参数写进 DMA 控制器:读还是写、内存起址、传多少。之后 DMA 控制器自己在设备和内存之间一字一字搬,整块搬完才发一次中断。
限制:每次只能传连续的若干块,读到内存里也必须连续存放;要读多段不连续的数据,CPU 得分别发多条 I/O 指令、处理多次中断。
通道是一种专门负责 I/O 的处理机。CPU 只需发一条 I/O 指令,指明通道程序在内存中的位置和要操作的设备;通道执行这段通道程序,把一组数据块(可以不连续)传完,才向 CPU 发中断。
和 CPU 的区别:通道指令类型单一,只能做 I/O;没有自己的内存,通道程序放在主存里,和 CPU 共享主存。
通道按信息交换方式分:字节多路通道(连多台低速设备)、数组选择通道(一次只服务一台高速设备)、数组多路通道(多台高速设备,按块轮流)。
| 方式 | CPU 干预频率 | 每次传送单位 | 数据流向 | CPU 与 I/O 并行度 |
|---|---|---|---|---|
| 程序直接控制 | 极高:全程盯着(忙等) | 字(字节) | 设备 → CPU 寄存器 → 内存 | 串行 |
| 中断驱动 | 高:每个字一次中断 | 字(字节) | 设备 → CPU 寄存器 → 内存 | 可并行(传数据时仍要 CPU) |
| DMA | 中:每块开始和结束时 | 块(连续的一块或多块) | 设备 → 内存(经 DMA 控制器,不经 CPU) | 较高 |
| 通道 | 低:一组块传完才中断 | 一组数据块 | 设备 → 内存(由通道控制) | 最高 |
中断和 DMA 的题目长得不一样,但都套同一个框架。区别只在“一次”传多少数据:中断是一个字,DMA 是一整块。
主频 500 MHz,CPI = 5。外设数据传输率 0.5 MB/s,以 32 位为单位中断传送。中断服务程序 18 条指令,其他开销相当于 2 条指令。
同一台机器,外设换成 5 MB/s,用 DMA,每块 5000 B,每次 DMA 的预处理 + 后处理共 500 个时钟周期。DMA 传送本身不占 CPU 时间。
如果 5 MB/s 的设备也用例 1 的中断方式:每秒中断 5×106 ÷ 4 = 1.25×106 次,占用 1.25×106 × 100 = 1.25×108 周期,占用率 25%。换成 DMA 只剩 0.1%:数据率涨了 10 倍,CPU 却只在每 5000 B 介入一次。
“某功能属于哪一层”是高频选择题。判据只有一条:涉及具体设备的硬件细节、且与中断无关的,在设备驱动程序;不涉及硬件、所有设备都要做的管理工作,在设备独立性软件。
printf)把请求翻译成系统调用;read / write 等系统调用);控制器执行命令、完成实际的数据传送,结束时发中断信号。
请求自上而下,结果自下而上返回。常考的顺序题:用户程序 → 系统调用处理程序 → 设备驱动程序 → 中断处理程序。
get / put 系统调用,读写一个字符(键盘、打印机);read / write 读写一块,seek 修改读写指针(可寻址);socket / bind / connect / read / write。驱动程序对下各不相同,对上要符合操作系统规定的统一接口,否则 OS 调不了它。
scanf),没有输入就一直等;阻塞是说“进程等不等”,和“CPU 忙不忙等”是两件事:阻塞的进程让出 CPU,不占 CPU。
缓冲区是内存中的一块区域(也可以是控制器里的寄存器)。它的规则只有一条:非空时不能往里冲入数据,只能取出;空了才能冲入,而且要冲满才能取出。下面的时间线全从这条规则推出来。
所以稳定后每个周期 = max(C, T) + M。
推论:当 C ≥ T 时,两个公式都等于 C + M,双缓冲没有收益(瓶颈在 CPU)。
多个等大缓冲区连成环。in 指针指向下一个可以冲入数据的空缓冲区,out 指针指向下一个可以取出数据的满缓冲区。本质是生产者—消费者问题的有界缓冲区。
全系统共用。三个队列:空缓冲队列、装满输入数据的队列、装满输出数据的队列。四种工作缓冲区:收容输入(hin)、提取输入(sin)、收容输出(hout)、提取输出(sout)。按需从队列摘取,用完挂回相应队列。
| 高速缓存(Cache) | 缓冲区(Buffer) | |
|---|---|---|
| 相同点 | 都介于高速设备和低速设备之间 | |
| 存放的数据 | 低速设备上某些数据的副本:Cache 里有的,低速设备上一定有 | 低速设备和高速设备之间正在传递的数据,不一定在低速设备上有备份 |
| 目的 | 存放高速设备经常要访问的数据;没命中时,高速设备还是要去访问低速设备 | 高速设备和低速设备的通信都要经过缓冲区,高速设备永远不直接访问低速设备 |
一个通道连多个控制器,一个控制器连多台设备。数据要走通 “设备 → 控制器 → 通道 → 内存” 这条路,所以三样都分到了,这次分配才算成功。
三者都分到,才启动 I/O。回收时按相反方向释放,并唤醒各自等待队列里的进程。
上面的做法要用户写物理设备名,换一台设备程序就得改;某台忙时,同类的另一台空着也用不上。
改进:用户只写逻辑设备名(“打印机”),系统在 SDT 中找第一台该类型且空闲的设备分配,再在逻辑设备表 LUT 里登记:
LUT 可以全系统一张(逻辑设备名不能重名,适合单用户)或每个用户一张(登录时建立,多用户系统用)。
早年的脱机技术用一台外围控制机把慢设备的数据先抄到磁带上。SPOOLing 用软件模拟这件事:输入进程、输出进程代替外围控制机,磁盘上的输入井、输出井代替磁带。前提是有多道程序技术。
不需要外围控制机,这是它和脱机技术的区别。
为什么柱面号放最高位:读地址连续的块时,先把同一柱面的各个盘面读完,磁臂不用动。换成 (盘面号, 柱面号, 扇区号) 就要频繁移磁臂。
旋转延迟和传输时间只和转速有关,OS 管不了。磁盘调度算法只优化寻道时间。
基于闪存,没有机械部件,随机访问快。由闪存翻译层 FTL(把逻辑块号翻译成物理页地址,相当于磁盘控制器)和闪存芯片组成。一个块包含若干页。
横轴是磁道号,纵轴从上往下是访问顺序,折线就是磁头的轨迹。空心圆是磁头初始位置,方块是走到磁盘端点、没有服务任何请求的空行程拐点,虚线是 C-SCAN / C-LOOK 的返回段。
| 算法 | 怎么走 | 优点 | 缺点 |
|---|---|---|---|
| FCFS | 按请求到达顺序 | 公平,实现简单 | 请求分散时性能差,接近随机 |
| SSTF | 每次选离当前磁头最近的请求 | 平均寻道短 | 只顾眼前(贪心),可能饥饿 |
| SCAN | 沿当前方向服务,到磁盘端点才掉头 | 不会饥饿,性能较好 | 到端点的空行程;两端磁道响应慢、不均匀 |
| C-SCAN | 只沿一个方向服务,到端点后直接回到另一端,返回途中不服务 | 各磁道等待时间更均匀 | 返回的空行程 |
| LOOK | SCAN 的改进:到当前方向最远的请求就掉头 | 省掉到端点的空行程 | 仍然两端不均匀 |
| C-LOOK | C-SCAN 的改进:到最远请求后,直接回到另一侧最远的请求 | 均匀,空行程更短 | 仍有返回段 |
SSTF 遇到两个请求距离相等时,本模拟器优先沿当前移动方向走。
| 功能 / 操作 | 属于哪一层 | 判断理由 |
|---|---|---|
| SPOOLing 假脱机 | 用户层软件 | 用户进程级的服务,建立在设备独立性软件之上 |
| 把二进制整数转成 ASCII 再打印 | 用户层软件 | 库函数做的格式化,与设备无关也不需要内核 |
| 库函数 printf / scanf | 用户层软件 | 把请求翻译成系统调用 |
| 统一的 read / write 接口(系统调用处理) | 设备独立性软件 | 对所有设备一样 |
| 设备保护(检查用户有无权限) | 设备独立性软件 | 不涉及硬件细节,所有设备都要做 |
| 逻辑设备名 → 物理设备名(LUT) | 设备独立性软件 | “设备独立性”这个名字的来源 |
| 设备的分配与回收 | 设备独立性软件 | 管理工作,与具体设备型号无关 |
| 缓冲区管理 | 设备独立性软件 | 所有设备都要用 |
| 与设备无关的差错处理 | 设备独立性软件 | 通用的错误报告和处理 |
| 由块号计算柱面号、磁头号、扇区号 | 设备驱动程序 | 要知道这块磁盘的具体几何参数 |
| 向设备寄存器写命令、设置参数 | 设备驱动程序 | 涉及具体硬件,且与中断无关 |
| 检查设备状态、启动设备 | 设备驱动程序 | 涉及具体硬件 |
| 保存现场、分析中断原因、恢复现场 | 中断处理程序 | 中断相关 |
| I/O 完成后唤醒被阻塞的进程 | 中断处理程序 | 由完成中断触发 |
| 方式 | 中断次数 | 传送单位 | 数据经过 CPU? | CPU 与 I/O | 典型用途 |
|---|---|---|---|---|---|
| 程序直接控制 | 无中断(CPU 轮询) | 字 | 是 | 串行 | 早期简单系统 |
| 中断驱动 | 每个字一次 | 字 | 是 | 并行 | 键盘等字符设备 |
| DMA | 每块一次(开始预处理 + 结束中断) | 块 | 否,设备 ↔ 内存直接传 | 并行度较高 | 磁盘等块设备 |
| 通道 | 一组块一次 | 一组块 | 否,通道执行通道程序 | 并行度最高 | 大型机多设备 |