第三part持久化,开始讲设备I/O了。
体系结构
整个I/O的体系结构基本如图。
- CPU通过一些专门的高速线路,直接连接显卡和内存
- 再通过DMI(Direct Media Interface),与专门的IO芯片相连
- 其他设备通过与IO芯片相连,以此连接到CPU(如网络的PCIE,USB的键鼠,eSATA硬盘)
硬件连接相关
硬件如何和OS交互?
硬件设备通过遵守某种规范的协议与OS进行交互,硬件本身会有一些寄存器位或者自己的芯片,通过寄存器位的数据进行出入参以及指令的控制,并由硬件的芯片去执行具体的操作。
除了通过寄存器交互外,还有一种交互方式,即内存映射,在内存中直接映射一块内存区域,这块内存区域对应了这个设备的内存,OS和设备通过这部分内存,来完成通信以及交互。
硬件和OS交互的性能
硬件和OS交互的话,最基本的话就是通过上图的那个方式,通过轮询status获取硬件设备的可读可写状态,然后从Data写入参数或者读取返回,通过Command写入需要硬件去执行的指令。
优化:
OS如何通过Status来感知设备状态?朴素的方式就是轮询,但是轮询会带来大量的CPU空转,所以解决方案就是:使用中断。
操作系统可以在发起IO后直接继续CPU操作,等待设备发起中断,以此来再去进行后续操作(而不是轮询等待IO完成),这样CPU计算和IO就可以并行执行了。
再次优化:
在IO比较快的情况下,频繁的中断又会有大量的上下文切换开销,所有我们又可以使用老思路来优化:两阶段,我们先进行一定次数的轮询,然后再进入等待中断。
再次优化-DMA:
更进一步,在最普遍的场景下(即内存落盘),是需要CPU持续从内存中搬运数据并写到磁盘中的,面对这种情况,工程师们设计了一个专门用于搬运的硬件-DMA(Direct Memory Access直接内存访问)。DMA是一种特殊的设备,专门用于将内存中指定位置的内存,写入到硬盘的指定位置中去,借住DMA设备,CPU在需要进行类似大量落盘的操作时,可以丢给DMA一个指令,自己继续去进行计算工作了。
驱动
连接OS和硬件设备之间的程序,我们称之为驱动这个概念应该基本都有认识,下到键盘鼠标耳机,上到网卡显卡声卡,操作系统装完机的第一件事基本都是先装驱动。
驱动的体系结构
驱动的规范也分几层
- 应用和操作系统之间,即API到Generic Block Interface,这层是用户api到操作系统之间的规范定义,由操作系统提供了标准的接口,然后再包装成一些易用的API给用户程序去使用
- 操作系统与实际驱动程序之间,这一层是操作系统将一些实际驱动程序提供的能力,封装成操作系统通用的接口
为什么这么设计?我们可以举个例子:
- 文件系统对通用规范发起操作,不需要知道实际读写的是什么设备
- 各种磁盘设备(比如HDD和SDD),他们实现各自驱动以满足操作系统的通用规范,以此来使得文件系统可以使用通用规范对他们进行读写
- 同时,通用规范可以通过接口的定义控制,来使得一些程序可以访问更加特殊的功能,例如一些程序需要进行磁盘碎片整理,或者获取某些设备的一些参数。
驱动的实现的例子
这里直接贴设计和代码,挺好理解的,就是针对设备的寄存器以及指令,实现操作系统的接口
设备的定义
上面是设备的定义,可以看到几个寄存器,其中有表示状态的,有表示错误的,有表示指令的
根据定义去实现接口
上图是部分简单的实现,一般来说一个驱动需要实现以下功能:
- Wait for drive to be ready. 等待设备就续
- Write parameters to command registers 将参数(入参,比如需要访问的扇区、块、访问的大小)写到寄存器
- Start the I/O. 通过指令寄存器,发起io申请
- Data transfer (for writes): 等待设备读写完成
- Handle interrupts. 处理中断
- Error handling. 处理异常