操作系统之I/O系统
CPU与I/O之间是如何提高效率的?
- 使用缓存技术(Buffering)
- 在内存中设置缓冲区,暂存I/O数据
- 减少CPU直接等待I/O设备的时间
- 包括,单缓存,双缓存,循环缓存等
- DMA(直接内存访问)
- 允许I/O设备直接与内存交换数据,不需要CPU全程参与
- CPU只需在传输开始和结束时介入
- 大幅减少CPU在I/O操作中的占用时间
- 中断机制
- CPU发出I/O请求后继续执行其他任务
- I/O完成后通过中断通知CPU
- 避免CPU轮询等待,提高CPU利用率
- 通道技术(I/O Channel)
- 使用专门的I/O处理器来管理I/O操作
- CPU只需发出I/O指令,具体执行由通道完成
- 进一步解放CPU
- 假脱机技术(Spooling)
- 将低速I/O设备虚拟成高速设备
- 典型应用是打印机缓存
- 数据先写入磁盘,再由系统统一调度输出
- I/O调度算法
- 优化I/O请求的执行顺序
- 例如磁盘调度算法(SCAN,C-SCAN)
- 减少磁盘寻道时间,提高整体效率
DMA技术提升I/O效率的原理是?
传统的I/O方式
在没有DMA的情况下:
- CPU发出I/O指令
- CPU逐字节/逐字从I/O设备读取数据
- CPU将每个数据写入内存
- 重复步骤2-3直到传输完成
这个过程中,CPU被完全占用,不能做其他工作。
DMA工作原理
传输前:
CPU向DMA控制器发送三个参数
- 源地址(I/O设备地址)
- 目标地址(内存地址)
- 传输数据量
传输中:
DMA控制器接管总线,直接在I/O设备和内存之间传输数据。CPU可以继续执行其他任务,DMA控制器每传输一个数据块,计数器减1
传输后:
传输完成后,DMA向CPU发送中断信号,CPU处理后续工作
效率提升体现
- 减少CPU干预次数
- CPU与I/O并行工作
- 减少CPU周期浪费
- 支持块传输
DMA的工作模式
- 停止模式:DMA传输时完全占用总线
- 周期窃取模式:DMA每传输一个字就释放总线
- 交替模式:CPU和DMA交替使用总线
缓冲与缓存机制的设计原理
缓冲(Buffer)和缓存(Cache)的区别
- 缓冲(Buffer):解决速度不匹配问题,强调数据的流动
- 缓存(Cache):解决访问效率问题,强调数据的重用
缓冲(Buffer)机制
目的:
- 协调速度不匹配的设备
- 减少I/O操作次数
- 平滑数据流
核心原理
- 单缓冲:[设备] → [缓冲区] → [用户区]
- 设备向缓冲区写入数据
- 用户从缓冲区读取数据
- 问题:设备写入时用户不能读取
- 双缓冲(常用):[设备] → [缓冲区1] ⇄ [缓冲区2] → [用户区]
- 设备向缓冲区1写入时,用户从缓冲区2读取
- 写满后交换角色
- 实现真正并行
- 循环缓冲区:[缓冲1] → [缓冲2] → [缓冲3] → … → [缓冲N] → [缓冲1]
- 多个缓冲区组成环形队列
- 生产者和消费者可以同时工作
- 适合流媒体等场景
缓存(Cache)机制
目的:
- 加速数据访问
- 减少访问慢速存储的次数
- 利用程序的局部性原理
核心原理
局部性原理:时间局部性和空间局部性
缓存结构:
CPU → [L1 Cache] → [L2 Cache] → [L3 Cache] → [主存] → [磁盘]
(最快) (最慢)
(最小) (最大)
缓存映射方式
-
直接映射:
内存块地址 % 缓存行数 = 缓存位置优点:查找速度快
缺点:冲突率高
-
全相联映射
内存块可以放在缓存的任意位置
优点:冲突率低
缺点:查找速度慢,硬件复杂
-
组相联映射
组号 = 内存块地址 % 组数 组内可以放在任意位置(如2路、4路)折中方案,最常用,平衡了速度和冲突率
缓存替换算法
- LRU(Least Recently Used)
- 替换最久未使用的数据
- 效果好但实现复杂
- LFU(Least Frequently Used)
- 替换访问次数最少的数据
- 需要维护计数器
- FIFO(First In First Out)
- 替换最早进入的数据
- 简单但效果一般
- Clock算法(Second Chance)
- FIFO的改进版
- 给每个页面一个"第二次机会"
写策略
写回(Write Back)
- 只写入缓存,延迟写入主存
- 用脏位(Dirty Bit)标记
- 优点:速度快
- 缺点:数据可能丢失
写直达(Write Through)
- 同时写入缓存和主存
- 优点:数据一致性好
- 缺点:速度慢
设备驱动与内核交互机制
I/O软件层次结构
┌─────────────────────────────┐
│ 用户进程(应用程序) │
├─────────────────────────────┤
│ 用户级I/O软件(库函数) │ 如:printf, fread
├─────────────────────────────┤
│ 设备无关的I/O软件(内核层) │ 如:VFS虚拟文件系统
├─────────────────────────────┤
│ 设备驱动程序(内核模块) │ ← 核心层
├─────────────────────────────┤
│ 中断处理程序(中断上下文) │
├─────────────────────────────┤
│ 硬件设备(外设) │
└─────────────────────────────┘
驱动的核心组成
- 初始化和清理:设备探测和设备移除
- 核心I/O操作
- 中断处理
- 电源管理
完整的I/O操作流程
场景:用户程序读取磁盘数据
- 用户空间发起请求
- 系统调用进入内核
- 触发软中断,CPU从用户态切换到内核态
- 系统调用处理程序接管
- 根据文件描述符找到对应的设备
- VFS层处理
- 设备驱动层处理
- 检查参数合法性
- 准备DMA传输
- 向硬件寄存器发送指令
- 等待I/O操作
- 硬件执行I/O操作
- 磁盘控制器读取数据
- 通过DMA直接写入内存
- 完成后发送硬件中断
- 中断处理
- 读取设备状态寄存器
- 判断中断类型
- 清除中断标志
- 唤醒等待队列
- 返回用户空间
- 数据复制到用户缓存区
- 回复用户进程执行
- read()系统调用返回
完整的流程
用户态:read() 系统调用
↓
────────────────(系统调用)────────────────
↓
内核态:sys_read() → vfs_read()
↓
VFS层:查找 file_operations
↓
驱动层:my_driver_read()
├─ 准备DMA
├─ 写硬件寄存器
└─ wait_event(等待) ← ─ ─ ─ ─ ┐
↓ │
硬件层:设备执行I/O │
↓ │
硬件中断:IRQ触发 │
↓ │
中断处理:my_driver_interrupt() │
├─ 读状态 │
├─ 清中断 │
└─ wake_up() ─ ─ ─ ─ ─ ─ ─ ┘
↓
返回用户态:copy_to_user()
同步I/O和异步I/O的区别
同步I/O(Synchronous I/O)
- 发起I/O请求后,必须等待I/O操作完成才能继续执行
- 进程/线程被阻塞或需要主动轮询
- 简单直观,编程模型简单
异步I/O(Asynchronous I/O)
- 发起I/O请求后,立即返回,不等待I/O完成
- 进程/线程可以继续执行其他任务
- I/O完成后通过回调/信号等方式通知
- 编程复杂,但效率更高

被折叠的 条评论
为什么被折叠?



