设备管理

1 设备引入*

典型的系统架构:通用 I/O 总线和多个外围总线用于连接设备

  • 物理和成本:总线速度越快,长度就必须越短
  • 需要高性能的设备更靠近 CPU

现代系统通常使用专用芯片组来提高性能

I/O 设备大体可以分类为:

  • 块设备
    • 以固定大小的块存储信息
    • 传输单位为整个块
  • 字符设备
    • 传递或接受字符流
    • 不可寻址,没有任何寻址操作

一个典型设备有两个重要组成部分

  • 向系统其余部分呈现的硬件接口
  • 内部结构

设备接口由几个寄存器组成

  • 状态寄存器:读取以查看设备的当前状态
  • 命令寄存器:指示设备执行特定任务
  • 数据寄存器:将数据传递给设备或从设备获取数据

alt text

CPU 和设备的通信:与控制器交互,以两种方式访问寄存器:

  • 端口映射 I/O:提供额外的 I/O 指令
    • 每个控制寄存器被分配一个 I/O 端口号
    • 使用特殊的 I/O 指令(inout
    • 这些指令通常是特权指令
  • 内存映射 I/O:将所有控制寄存器映射到内存空间中
    • 每个控制寄存器被分配一个唯一的内存地址
    • 为了访问特定的寄存器,操作系统发出一个 load 指令(读取)或 store 指令(写入)该地址
    • 然后硬件将 load/store指 令指向到设备而不是主存储器
内存映射时小心缓存
Loop: test memory_mapped_io_arddress //check the status of device
      jz ready // if it is 0, go to ready
      goto loop // otherwise, continue testing
ready:
  • 第一次 test 这个 address 的值会被缓存到 cache 中
  • 之后的轮训 test 是直接从 cache 中取值,无法反映设备的真正状态
  • 因此一般要禁掉相应内存地址的缓存

在发出与设备通信的指令后,操作系统需要获知状态(完成/错误),两种方法:

  • 轮询:操作系统定期检查设备特定的状态寄存器
    • 开销低(没有切换),但如果是低速的设备上会浪费 CPU 周期
  • I/O 中断:设备在需要服务时生成中断
    • 不会浪费 CPU 周期,但是开销高(伴随切换)

一般来说两种混合

谁来控制数据传输命令?—— CPU 或 DMA

由 CPU 来控制

  • 即 Programmed I/O
  • 直接通过处理器的 in/outload/store 传输每个字节
  • 优点:硬件简单,易于编程
  • 消耗与数据大小成比例的处理器周期(因为每个数据的 in/out 都要一个指令)
  • 此外,CPU 会一直接收到中断,速度变慢

由直接内存访问(DMA)控制

  • 给控制器访问内存和总线的权限
  • 要求它直接在内存和控制器之间传输数据块

alt text

计算机系统内部不只是有通用的 CPU

  • 专门负责 memcpy() 的 CPU:DMA
  • 专门负责解析和显示图形的 CPU:GPU
  • 很多设备都有自己的逻辑处理芯片(比如磁盘)
  • I/O 管理可以看成是中央芯片(CPU)和外部芯片的交流

人手一个通用 CPU?通用性增加了很多设计以及与之而来的功耗

2 硬件抽象 —— 设备驱动程序

设备驱动程序是操作系统和物理硬件之间沟通的 “桥梁”

世界上有成千上万种硬件设备,它们由不同的厂商制造。操作系统不可能(也不应该)把所有硬件的具体控制细节都内置在内核中。因此,操作系统通过设备驱动程序来与这些硬件进行交互。

  • 操作系统创建了一个分层的存储视图
  • 分层方法允许更低层部分容易更改
    • 例如,文件系统的实现独立于磁盘类型
  • 管理 I/O 设备的代码在内核设备驱动程序
    • 设备驱动程序是内核错误的最常见来源
    • 这是由于给定的驱动程序可能只被少数系统使用,所以它不会被高度使用或仔细检查

alt text

设备驱动程序通常分为两部分:

  • 上半部分:在系统调用的调用路径中访问
    • 实现一组标准的、跨设备的调用,如 open()close()read()write()
    • 这是内核与设备驱动程序的接口
    • 上半部分将启动设备的 I/O 操作,可能会让线程休眠直到完成(同步和异步I/O)
  • 下半部分:作为中断例程运行
    • 获取输入或传输下一块输出
    • 如果 I/O 现在完成,可能会唤醒休眠的线程

一次 IO 请求的生命周期如下

alt text

3 磁盘

将数据磁性地储存在与玻璃、陶瓷或铝等材料的旋转盘上粘合的薄金属膜上

alt text

3.1 磁盘读写

  • 磁头移动到适当的磁道
    • 寻道时间
    • 稳定
    • 启用适当的磁头
  • 等待扇区出现在磁头下
    • 旋转延迟
  • 读取/写入扇区
    • 传输时间

alt text

  • 磁盘访问时间:\(T_{I/O} = T_{seek} + T_{rotation} + T_{transfer}\)
  • 磁盘传输速率:\(R_{I/O} = \dfrac{Size_{transfer}}{T_{I/O}}\)
  • 在顺序工作负载中传输 100 MB 数据
    • \(T_{seek} = 4\ \text{ms}\) (average seek)
    • \(T_{rotation} = 2\ \text{ms}\) (average rotation)
    • \(T_{transfer} = 0.8\ \text{s}\)
    • \(R_{I/O} = 100\ \text{MB} / 0.806\ \text{s} = 124\ \text{MB/s}\)

磁道偏移:确保在跨越磁道边界时能够正确处理顺序读取

  • 从一个磁道切换到另一个磁道时,磁盘需要时间重新定位磁头
  • 如果没有这样的偏移,磁头会被移动到下一个磁道,但所需的下一个数据块已经旋转到磁头后面了(得重新转一圈才能读到)

多区域磁盘驱动器:外部磁道比内部磁道有更多的扇区

  • 磁盘被组织成多个区域,每个区域是表面上一组连续的磁道
  • 每个区域的每条磁道的扇区数相同
  • 现代磁盘支持逻辑块寻址(不考虑磁盘几何结构)

alt text

缓存(磁道缓冲区):一些小容量的内存,用于存储从磁盘读取或写入到磁盘的数据

  • 读取扇区时,会读取该磁道上的所有扇区
  • 写入时,有两种写入策略:当数据被放入缓存时(写回,write back)或数据实际写入磁盘后(直写,write through)才确认写入
  • 缓冲区在磁盘的逻辑板上

3.2 磁头调度

目标:通过磁头调度来最小化磁头移动,从而最大化磁盘 I/O 吞吐量

3.2.1. 先到先服务(FCFS)

按请求到达的顺序处理磁盘请求

alt text

3.2.2. 最短寻道时间优先(SSTF)

按磁道顺序排列 I/O 请求队列,优先处理最接近磁道的请求。

  • 最小化寻道时间
  • 假设当前磁头位置在内轨道上,则依次处理请求 35(中间),然后再处理请求 6(外部)

假设第一个请求是磁道(柱面)11,随后依次有新请求进入,分别是磁道 1、36、16、34、9 和 12

  • 最短寻道时间优先:1 + 3 + 7 + 15 + 33 + 2 = 61 个磁道

alt text

3.2.3. 电梯算法(扫描算法,SCAN)

简单地在磁盘上来回移动,按顺序跨磁道处理请求(像电梯一样运行)

  • 从磁盘的一端(从外轨到内轨或从内轨到外轨)的单次通过称为一次扫描
  • 磁臂移动距离为:1 + 4 + 18 + 2 + 27 + 8 = 60 个磁道

alt text

有多种变体可用:

  • F-SCAN:在执行扫描时暂时冻结要处理的队列(避免远距离请求的饥饿)
  • C-SCAN:只从外轨到内轨扫描,然后重置到外轨重新开始
    • 更平均的等待时间,即对内轨和外轨更公平

3.2.4. 最短定位时间优先(SPTF)

同时考虑寻道时间和旋转时间

  • 假设当前磁头位置在第 42 扇区(内轨),那么是处理请求 32(中间)还是请求 13(外轨)?
  • 如果寻道时间远高于旋转延迟,那么最短寻道优先(SSF)是好的选择
  • 但如果寻道速度比旋转快得多,那么应先处理请求 13

alt text

在操作系统中实现这一点很困难,因为操作系统通常不清磁头当前的具体位置

操作系统同样利用抽象技术管理 I/O
  • 各种设备驱动程序就是为了实现统一的接口
  • 另外一个视角:I/O 就是各个 CPU(通用和专用)的交流,驱动程序就是解释器

标题:设备管理

作者:Zwing

创建于:2026-08-09 00:07:00

更新于:2026-08-08 16:25:54

链接:https://zanytriumph.github.io/posts/持久化-设备管理.html

版权声明:本文章采用 CC BY-NC-SA 4.0 进行许可