越来越多的 I/O 设备

如果你只造 “一台计算机”

  • 随便给每个设备定一个端口/地址,用 mux 连接到 CPU 就行
    • 你们的实验 (AbstractMachine) 和自制 CPU 就是这么做的

但如果你希望给未来留点空间?

  • 想卖大价钱的 “大型机”
    • IBM, DEC, ...
  • 车库里造出来的 “微型机”
    • 名垂青史的梦想家
  • 都希望接入更多 I/O 设备
    • 甚至是未知的设备,但不希望改变 CPU

总线:一个特殊的 I/O 设备

提供设备的注册地址到设备的转发

  • 把收到的地址 (总线地址) 和数据转发到相应的设备上
  • 例子: port I/O 的端口就是总线上的地址
    • IBM PC 的 CPU 其实只看到这一个 I/O 设备

这样 CPU 只需要直连一个总线 就行了!

  • 今天 PCI 总线肩负了这个任务
    • 总线可以桥接其他总线 (例如 PCI → USB)
  • lspci -tvlsusb -tv: 查看系统中总线上的设备
    • 概念简单,实际非常复杂……

中断控制器

CPU 有一个中断引脚

  • 收到某个特定的电信号会触发中断
    • 保存 5 个寄存器 (cs, rip, rflags, ss, rsp)
    • 跳转到中断向量表对应项执行

系统中的其他设备可以向中断控制器连线

  • Intel 8259 PIC
    • programmable interrupt controller
    • 可以设置中断屏蔽、中断触发等……
  • APIC (Advanced PIC)
    • local APIC: 中断向量表, IPI, 时钟, ……
    • I/O APIC: 其他 I/O 设备

中断没能解的问题

假设程序希望写入 1 GB 的数据到磁盘

  • 即便磁盘已经准备好,依然需要非常浪费缓慢的循环
  • out 指令写入的是设备缓冲区,需要去总线上绕一圈
    • cache disable; store 其实很慢的
for (int i = 0; i < 1 GB / 4; i++) {
  outl(PORT, ((u32 *)buf)[i]);
}

能否把 CPU 从执行循环中解放出来?

  • 比如,在系统里征用一个小 CPU,专门复制数据?
  • 好像 memcpy_to_port(ATA0, buf, length);

Direct Memory Access (DMA)

DMA: 一个专门执行 “memcpy” 程序的 CPU

  • 加一个通用处理器太浪费,不如加一个简单的

支持的几种 memcpy

  • memory → memory
  • memory → device (register)
  • device (register) → memory
    • 实际实现:直接把 DMA 控制器连接在总线和内存上
    • Intel 8237A

PCI 总线支持 DMA

  • sudo cat /proc/iomem