目录
前言
本文是学习《大话计算机》的学习笔记之一,之前许多不清楚的细节,能够理解清楚了。
所谓IO,可以简单理解为:硬盘、网卡、鼠标键盘、显卡等设备的数据输入输出。从CPU的角度来看,它可以直接访问的是主存、一些外部寄存器等,因为上述的外部输入输出设备速度相较于CPU还是慢了很多很多,所以需要通过IO子系统进行统一管理。
Tips:CPU可以访问的空间叫做:全局地址空间,包括主存+外部寄存器。通过Load/Stor指令访问,CPU内部的寄存区当然是专有指令访问啦,比如LDPTB指令用于加载页表基地址,如果用Load/Stor访问,MMU从哪里获取基址呢?所以它是专有命令加载基址,MMU才能工作。
1. 无DMA参与的硬盘访问
在这里先考虑DMA的参与,描述一下最简单的硬盘IO过程:
程序将数据写入内存 -> 将写指令Stor到IO控制器的寄存器中 -> 将主存中的数据在Load/Stor到IO控制器缓存中(这个缓存在全局地址空间) -> IO控制器完成数据的写入(此过程较慢,CPU去处理其他任务) -> 写完后,IO控制器相关寄存器置位"Finished",等待程序来检查标志位或者通过中断的方式通知,写数据完成了。附上一张草图。
Tips: IO控制器一般位于主板上,或者是单独的适配卡,例如网卡,显卡,声卡等
2. 有DMA引擎的硬盘访问过程
DMA的出现如同其他硬件一样是为了卸载CPU的负载(offload,业界一直在做的事情),毕竟用CPU资源来搬运数据太浪费资源了。DMA负责搬运数据,它可以直接访问内存和设备的缓存,并完成数据的搬移。
较上图增加了DMA模块,控制电路会配置基址与长度,然后DMA引擎便不断搬运数据,直到任务完成。
至此总结下控制器需要暴露的寄存器有:控制寄存器、命令寄存器、数据寄存器、状态寄存器。kernel将命令所在内存地址赋值到命令寄存器,数据所在内存地址赋值到数据寄存器,然后将控制寄存器置位,总控制模块设置DMA从内存中读取相应命令并解析、执行。
3. Polling + Interrupt
在控制器完成IO任务后,如何通知相关程序呢?这里有两种方法:
1. 程序不断轮询(polling)控制器的状态寄存器,待其变为"Finished"之后,任务分成。
2. 中断方式,程序发起读写操作后,将自己挂起,并告诉调度器,某个事件发生时,在唤醒我,当然了,中断方式肯定是需要DMA来辅助的。此时无需暴露控制器内部的缓存。
4. 使用队列提升IO性能
此部分待更新。。。