OS-IO

为什么需要中断

计算机中有大量的硬件,它们之间通过各种总线连接,比如:

  • CPU和内存通过某种专有的高速总线连接。
  • 显卡和现代的SSD等高速设备通过PCIe连接到CPU。
  • 鼠标、机械硬盘等缓慢的设备通过外设 I/O 总线连接到 CPU。

实际现代 CPU 将内存控制器集成于芯片,采用 DDR4/DDR5 通道,由内部互连(如 Intel UPI、AMD Infinity Fabric)管理多路内存访问。

graph LR
    CPU((CPU))
    内存[内存]

    subgraph 专有高速总线
        CPU <--双向传输--> 内存
    end

    subgraph PCI_Express["PCIe总线"]
        PCIe总线[PCIe总线] --> CPU
        显卡[[显卡]] --> PCIe总线
        NVMe_SSD[[NVMe SSD]] --> PCIe总线
    end

    subgraph 外设总线["外设I/O总线(如USB/SATA)"]
        外设控制器[I/O控制器] --> CPU
        鼠标[[鼠标]] --> 外设控制器
        机械硬盘[[机械硬盘]] --> 外设控制器
    end

    classDef cpu fill:#f9d71c,stroke:#333;
    classDef memory fill:#66ccff,stroke:#333;
    classDef pci fill:#90EE90,stroke:#333;
    classDef storage fill:#ff9999,stroke:#333;
    classDef io fill:#ffb366,stroke:#333;
    
    class CPU cpu;
    class 内存 memory;
    class PCIe总线,显卡,NVMe_SSD pci;
    class 机械硬盘 storage;
    class 外设控制器,鼠标 io;

而这些硬件可以被抽象为接口和内部实现:

  • 接口提供寄存器使得外部能够获取信息并操作硬件。
  • 内部实现使用者无需关心。

假设,接口提供了两类寄存器:

  • 状态寄存器(status):读取并查看当前的硬件状态。
  • 命令寄存器(command):让硬件执行具体的命令和操作。
  • 数据寄存器(data):发送或读取硬件相关的数据。

那么,一个典型的使用硬件的方式为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
while status==BUSY:
    pass    # 等待硬件空闲

with open(data,'w') as DATA:
    # send data

with open(command,'w') as COMMAND:
    # do some things

while status==BUSY:
    pass    # 等待硬件完成任务

with open(data,'r') as DATA:
    # read the data

不停的循环读取硬件状态被称为轮询(polling),显然这是浪费 CPU 时间的设计。就像之前并发控制中使用futex替换自旋锁一样,计算机也应该有某种方式主动通知程序任务已完成。

为此,工程师发明了中断

什么是中断

中断是硬件设备完成任务后主动通知 CPU,CPU再跳转到操作系统预先设定好的中断处理程序或中断服务例程(ISR)。

sequenceDiagram
    participant Device
    participant CPU
    participant ISR
    Device->>CPU: 发送中断信号(INT)
    CPU->>CPU: 保存上下文(寄存器状态)
    CPU->>ISR: 执行中断服务例程
    ISR->>Device: 读取状态/数据寄存器
    ISR->>CPU: 恢复上下文

不过,轮询并非一定比中断差:假设我们有一个高速设备,只需CPU一次轮询的时间就能完成命令,那么使用中断反而会导致频繁的进程切换从而降低性能。因此,如果设备很快,轮询反而是更好的方法。

同时,还存在混合模式:初期轮询一小段时间,之后超时转为中断模式。

进一步加速——DMA

从内存往硬盘拷贝数据是十分常见并且比较简单的任务,因此工程师设计了 DMA 硬件来完成这项任务而无需 CPU 介入。

使用 DMA 时,操作系统指示内存的位置、大小以及目标设备。接着,DMA 就会开始传输数据,而操作系统和 CPU 就可以执行其他任务。当 DMA 完成时再抛出中断通知操作系统任务已经完成了。

现代 DMA 控制器支持在一次传输中描述多个非连续内存片段,减少一次次配置开销。

sequenceDiagram
    participant 应用程序
    participant CPU
    participant DMA控制器
    participant 硬盘
    
    应用程序->>CPU: 调用write()函数
    CPU->>DMA控制器: 配置传输参数(地址/长度)
    CPU->>CPU: 立即返回执行其他任务
    DMA控制器->>硬盘: 启动数据传输
    硬盘-->>DMA控制器: 完成通知
    DMA控制器->>CPU: 触发IRQ完成中断
updatedupdated2025-05-072025-05-07