I/O 设备的抽象

I/O 设备模型:一个能与 CPU 交换数据的接口/控制器

  • 寄存器被映射到地址空间


操作系统:设备也是操作系统中的对象

  • 如何 “找到” 一个对象?
  • 对象支持什么操作?

I/O 设备的抽象 (cont'd)

I/O 设备的主要功能:输入和输出

  • “能够读 (read) 写 (write) 的字节序列 (流或数组)
  • 常见的设备都满足这个模型
    • 终端/串口 - 字节流
    • 打印机 - 字节流 (例如 PostScript 文件)
    • 硬盘 - 字节数组 (按块访问)
    • GPU - 字节流 (控制) + 字节数组 (显存)

操作系统:设备 = 支持各类操作的对象 (文件)

  • read - 从设备某个指定的位置读出数据
  • write - 向设备某个指定位置写入数据
  • ioctl - 读取/设置设备的状态

设备驱动程序

把系统调用 (read/write/ioctl/...) “翻译” 成与设备寄存器的交互

  • 就是一段普通的内核代码
  • 但可能会睡眠 (例如 P 信号量,等待中断中的 V 操作唤醒)

例子:/dev/ 中的对象

  • /dev/pts/[x] - pseudo terminal
  • /dev/zero - “零” 设备
  • /dev/null - “null” 设备
  • /dev/random, /dev/urandom - 随机数生成器
    • 试一试:head -c 512 [device] | xxd
    • 以及观察它们的 strace
      • 能看到访问设备的系统调用

例子: Lab 2 设备驱动

设备模型

  • 简化的假设
    • 设备从系统启动时就存在且不会消失
  • 支持读/写两种操作
    • 在无数据或数据未就绪时会等待 (P 操作)
typedef struct devops {
  int (*init)(device_t *dev);
  int (*read) (device_t *dev, int offset, void *buf, int count);
  int (*write)(device_t *dev, int offset, void *buf, int count);
} devops_t;

I/O 设备看起来是个 “黑盒子”

  • 写错任何代码就 simply “not work”
  • 设备驱动:Linux 内核中最多也是质量最低的代码

字节流/字节序列抽象的缺点

设备不仅仅是数据,还有控制

  • 尤其是设备的附加功能和配置
  • 所有额外功能全部依赖 ioctl
    • “Arguments, returns, and semantics of ioctl() vary according to the device driver in question”
    • 无比复杂的 “hidden specifications”

例子

  • 打印机的打印质量/进纸/双面控制、卡纸、清洁、自动装订……
    • 一台几十万的打印机可不是那么简单 😂
  • 键盘的跑马灯、重复速度、宏编程……
  • 磁盘的健康状况、缓存控制……

例子:终端

“字节流” 以内的功能

  • ANSI Escape Code
  • (还记得第一次课的那个数码管吗?)

“字节流” 以外的功能

  • stty -a
    • 终端大小怎么知道?
    • 终端大小变化又怎么知道?
  • isatty (3), termios (3)
    • 大部分都是 ioctl 实现的
    • 这才是水面下的冰山的一角