OS IO Software And Device Management
I/O 软件层次
I/O 软件把用户请求逐层转换成硬件能执行的操作。越上层越接近用户,越下层越接近硬件。
| 层次 | 位置 | 主要作用 |
|---|---|---|
| 用户层软件 | 用户态 | 提供库函数或用户接口,把用户请求转换成系统调用 |
| 设备独立性软件 | 内核态 | 提供统一 I/O 接口,完成设备保护、差错处理、设备分配、缓冲区管理、逻辑设备名映射 |
| 设备驱动程序 | 内核态 | 把通用 read/write 请求转换成特定设备能执行的寄存器操作 |
| 中断处理程序 | 内核态,最靠近硬件 | 响应设备中断,读控制器状态,完成中断应答和少量紧急处理 |
| 硬件 | 控制器和设备 | 执行实际 I/O 操作 |
直接涉及设备寄存器、设备状态位、设备厂商规定的控制细节,通常属于设备驱动程序。不关心具体硬件、对多类设备都需要的管理工作,通常属于设备独立性软件。
用户层软件给用户程序提供更方便的接口。用户程序通常不直接组织内核内部的 I/O 请求,而是调用库函数或系统调用封装。
例如输出文本时,程序可能调用 printf。用户层库函数会把格式化输出转换成 write 一类系统调用,再进入内核 I/O 软件。
用户层软件解决的是“怎样让用户方便地提出请求”,不是“怎样具体操作设备寄存器”。
设备独立性软件
设备独立性软件也叫设备无关性软件。它把具体设备差异压到驱动程序下面,对上提供统一接口。
常见功能:
| 功能 | 含义 |
|---|---|
| 统一调用接口 | 向上提供 read、write、open、close 等统一接口 |
| 设备保护 | 检查进程是否有权访问设备,防止非法 I/O |
| 差错处理 | 对可统一处理的设备错误做重试、报告、恢复或失败返回 |
| 设备分配与回收 | 按设备状态、控制器状态、通道状态决定能否分配 |
| 缓冲区管理 | 用内存缓冲区协调速度差异、传输单位差异 |
| 逻辑设备名映射 | 建立逻辑设备名到物理设备名、驱动入口的映射 |
设备独立性带来的效果是:用户程序不必知道“到底是哪一台打印机、哪个控制器、哪个状态位表示忙”。用户只面对逻辑设备名和统一调用接口。
逻辑设备名与 LUT
用户程序使用的是逻辑设备名,例如 /dev/printer。操作系统需要把它映射到具体物理设备,并找到对应驱动程序入口。
逻辑设备表 LUT 常见表项:
| 逻辑设备名 | 物理设备名 | 驱动程序入口地址 |
|---|---|---|
/dev/printer |
3 | 1024 |
/dev/tty |
5 | 2046 |
LUT 的设置方式:
| 设置方式 | 特点 | 适用 |
|---|---|---|
| 系统只有一张 LUT | 所有用户不能使用相同逻辑设备名 | 单用户系统 |
| 每个用户一张 LUT | 不同用户可使用相同逻辑设备名 | 多用户系统 |
第一次使用某类设备时,系统可根据逻辑设备名表示的设备类型在系统设备表中寻找空闲物理设备,分配成功后在 LUT 中增加表项。以后同一进程或用户再次使用该逻辑设备名时,系统可直接根据 LUT 找到物理设备和驱动入口。
设备驱动程序
设备驱动程序负责具体设备控制。上层发来的是统一请求,例如“读”“写”“启动”;驱动程序要把它翻译成该设备控制器能理解的寄存器操作。
驱动程序通常要做:
- 设置控制寄存器,如启动读写、设置方向、允许中断;
- 检查状态寄存器,如忙、空闲、就绪、错误;
- 从数据寄存器取数据,或向数据寄存器写数据;
- 按设备厂商规定解释状态位和控制位;
- 与中断处理程序配合,完成异步 I/O。
不同设备需要不同驱动程序,是因为控制器设计不同:寄存器数量可能不同,状态位含义可能相反,命令格式和时序也可能不同。设备独立性软件不能假设所有设备都有同一套寄存器约定。
中断处理程序
中断处理程序在 I/O 软件层次中最靠近硬件。当设备完成 I/O 或发生错误时,控制器向 CPU 提出中断请求;CPU 转入内核后执行相应中断处理程序。
I/O 中断处理程序通常做:
- 从控制器读出设备状态。
- 判断 I/O 是否正常完成。
- 若异常,按原因处理,例如重试、报告错误、唤醒等待进程并返回错误。
- 若正常,从设备或控制器读入数据,或确认 DMA/通道传送已经完成。
- 更新内核 I/O 请求状态。
- 唤醒等待该 I/O 的进程,或通知上层继续后续处理。
异常与中断处理流程见 Exception-And-Interrupt-Handling。
设备保护与差错处理
设备保护的核心是:用户程序不能绕过操作系统随意访问设备。设备通常由内核管理,用户请求要通过系统调用进入内核,由内核检查权限和设备状态。
设备保护常见控制点:
- 用户是否有权打开或操作该设备;
- 请求的读写方向是否允许;
- 独占设备是否已经被其他进程占用;
- 设备是否处于可用状态。
差错处理分为两类:
| 类型 | 例子 | 处理位置 |
|---|---|---|
| 与具体设备强相关 | 控制器状态位含义、设备复位时序、设备专用错误码 | 设备驱动程序 |
| 可统一处理 | 重试次数、错误上报、唤醒等待进程、返回错误状态 | 设备独立性软件 |
设备分配要考虑什么
设备分配不是只看设备本身是否空闲。一次 I/O 往往同时占用设备、控制器和通道,三者都可用时才能真正启动。
设备固有属性:
| 属性 | 含义 | 例子 |
|---|---|---|
| 独占设备 | 一个时段只能分配给一个进程 | 普通打印机 |
| 共享设备 | 可被多个进程宏观上同时使用 | 磁盘 |
| 虚拟设备 | 用软件把独占设备改造成逻辑上的共享设备 | SPOOLing 共享打印机 |
设备分配算法可采用先来先服务、优先级高者优先、短任务优先等。算法决定等待队列中哪个请求先获得资源。
从进程安全性看,还有两种方式:
| 方式 | 做法 | 特点 |
|---|---|---|
| 安全分配 | 分配设备后阻塞进程,本次 I/O 完成后再唤醒 | 不易发生死锁,但 CPU 与进程并行性较差 |
| 不安全分配 | 分配设备后进程可继续运行,之后还可再请求其他资源 | 并行性较好,但资源请求顺序不当可能死锁 |
设备分配数据结构
操作系统用多张表描述设备、控制器、通道及等待队列。
| 表 | 对象 | 典型内容 |
|---|---|---|
SDT 系统设备表 |
全系统设备总表 | 设备类型、设备标识符、DCT 指针、驱动程序入口 |
DCT 设备控制表 |
每个设备一张 | 设备类型、设备标识符、设备状态、COCT 指针、设备等待队列 |
COCT 控制器控制表 |
每个控制器一张 | 控制器标识符、控制器状态、CHCT 指针、控制器等待队列 |
CHCT 通道控制表 |
每个通道一张 | 通道标识符、通道状态、所连控制器信息、通道等待队列 |
设备分配与回收过程
使用物理设备名时,基本分配过程是:
- 根据进程请求的物理设备名查
SDT。 - 根据
SDT找到DCT。若设备忙,把进程 PCB 挂到设备等待队列;若空闲,分配设备。 - 根据
DCT找到COCT。若控制器忙,把进程 PCB 挂到控制器等待队列;若空闲,分配控制器。 - 根据
COCT找到CHCT。若通道忙,把进程 PCB 挂到通道等待队列;若空闲,分配通道。 - 设备、控制器、通道都分配成功后,启动 I/O。
使用逻辑设备名时,过程会先经过 LUT:
- 用户请求逻辑设备名。
- 若 LUT 已有映射,直接得到物理设备名和驱动入口。
- 若 LUT 没有映射,系统根据设备类型在
SDT中寻找空闲设备。 - 分配成功后,在 LUT 中建立逻辑设备名、物理设备名、驱动入口的映射。
- 再按
DCT -> COCT -> CHCT检查并分配控制器和通道。
回收过程方向相反:I/O 完成后释放设备、控制器、通道,更新相应表项状态;若等待队列非空,按分配算法唤醒或分配给下一个等待进程。
小结
- I/O 软件层次从上到下是:用户层软件、设备独立性软件、设备驱动程序、中断处理程序、硬件。
- 设备独立性软件负责统一接口、保护、差错处理、分配回收、缓冲和逻辑设备名映射。
- 设备驱动程序负责具体硬件细节,尤其是寄存器设置和状态解释。
- LUT 把逻辑设备名映射到物理设备名和驱动入口。
- 设备分配要同时考虑设备、控制器和通道,常通过
SDT -> DCT -> COCT -> CHCT逐层检查。