现代操作系统(四):输入 / 输出

除了提供抽象(进程、地址空间和文件等)以外,操作系统还要控制计算机的所有I/O设备。操作系统必须向设备发送命令,捕捉中断,并处理设备的各种错误。它还应该在设备和系统的其他部分之间提供简单且易于使用的接口。

1. I/O硬件原理

1.1 I/O设备

IO设备大致分为两类,块设备(block device)字符设备(character device)

块设备

  • 把信息存储在固定大小的块中,每个块有自己的地址。
  • 所有的传输以一个或者多个完整的(连续的)块为单位。
  • 块设备的基本特征是每个块都能独立于其他块而读写,
  • 硬盘、蓝光光盘和USB盘是最常见的块设备。

字符设备

  • 字符设备以字符为单位发送或接受一个字符流,而不考虑任何块结构。
  • 字符是不可寻址的,也没有任何寻道操作。
  • 打印机。网络接口。鼠标等都可看作字符设备。

1.2 设备控制器

  • I/O设备一般由机械部件和电子部件两部分组成,电子部件称作设备控制器(device controller)或适配器(adapter),机械部件则是设备本身。

  • 为了屏蔽设备之间的差异,每个设备都有⼀个叫设备控制器的组件,控制器与设备之间的接口通常是一个很低层次的接口。

  • 控制器的任务时把串行的位流转换为字节块,并进行必要的错误校正工作。字节块通常首先在控制器内部的一个缓冲区中按位进行组装,然后再对校验和进行校验并证明字节快没有错误后,将它复制到主存。

1.3 内存映射I/O

每个控制器有几个寄存器用来和CPU通信

  • 通过写入这些寄存器,操作系统可以命令设备发送数据,接收数据、开启或关闭、或者执行某些其他操作。
  • 通过读入这些寄存器,操作系统可以了解设备的状态,是否准备好接收一个新的命令。
  • 除了这些控制寄存器,许多设备还有一个操作系统可以读写的数据缓冲区。

控制器是有三类寄存器,它们分别是状态寄存器(Status Register)、 命令寄存器(Command Register)以及数据寄存器(Data Register)

  • 数据寄存器,CPU 向 I/O 设备写⼊需要传输的数据,⽐如要打印的内容是「Hello」,CPU 就要先发送⼀个 H 字符给到对应的 I/O 设备。
  • 命令寄存器,CPU 发送⼀个命令,告诉 I/O 设备,要进⾏输⼊/输出操作,于是就会交给I/O 设备去⼯作,任务完成后,会把状态寄存器⾥⾯的状态标记为完成。
  • 状态寄存器,⽬的是告诉 CPU ,现在已经在⼯作或⼯作已经完成,如果已经在⼯作状态,CPU 再发送数据或者命令过来,都是没有⽤的,直到前⾯的⼯作已经完成,状态寄存标记成已完成,CPU 才能发送下⼀个字符和命令

数据缓冲区主要是为了减少对设备的频繁操作

  • CPU 写⼊数据到控制器的缓冲区时,当缓冲区的数据囤够了⼀部分,才会发给设备。
  • CPU 从控制器的缓冲区读取数据时,也需要缓冲区囤够了⼀部分,才拷⻉到内存。

那CPU如何与设备的控制寄存器和数据缓冲区进行通信?

  • 端⼝ I/O:每个控制寄存器被分配⼀个 I/O 端⼝,可以通过特殊的汇编指令操作这些寄存器,⽐如 in/out 类似的指令。
  • 内存映射 I/O:将所有控制寄存器映射到内存空间中,这样就可以像读写内存⼀样读写数据缓冲区。

1.4 直接存储器存取

无论一个CPU是否具有内存映射I/O,它都需要寻址设备控制器以便与设备交换数据。CPU可以从I/O控制器每次请求一个字节的数据,但是这样做浪费CPU时间,所以经常用到一种称为直接存储器存取(Direct Memory Access,DMA) 的方案。它可以使得设备在 CPU 不参与的情况下,能够⾃⾏完成把设备 I/O 数据放⼊到内存。那要实现 DMA 功能要有 「DMA 控制器」硬件的⽀持。

DMA 的⼯作⽅式如下:

  • CPU通过设置DMA控制器的寄存器对它进行编程,通知它要读多少数据,要读到什么位置;
  • 接下来,DMA 控制器会向磁盘控制器发出指令,通知它从磁盘读数据到其内部的缓冲区中,接着磁盘控制器将缓冲区的数据传输到内存;
  • 当写操作完成时,磁盘控制器在总线上发出一个应答信号到DMA控制器,DMA控制器检查是否完成所有内容的写,如果没有继续重复上述过程;
  • 经过检查发现传送工作完成了之后,DMA 控制器发中断通知 CPU 指令完成,CPU 就可以直接⽤内存⾥⾯现成的数据了;

1.5 重温中断

在这里插入图片描述

  • 为了处理中断,中断控制器在地址线上放置一个数字表明哪个设备需要关注,并且置起一个中断CPU的信号。
  • 中断信号导致CPU停止当前正在做的工作并且开始做其他的事情。
  • 地址线上的数字被用作指向一个成为中断向量的表格的索引,以便读取一个新的程序计数器。

2. I/O软件原理

2.1 I/O软件的目标

  • 设备独立性(device independence)
    • 能够访问任意IO设备而无需事先指定设备。
    • 例如从从硬盘、DVD或者USB盘上读取文件不需要修改程序。
  • 统一命名(uniform naming)
    • 一个文件或一个设备的名字应该是一个简单的字符串或一个整数,它不应该依赖于设备。
    • 例如UNIX系统中,所有存储盘都能以任意方式集成到文件系统层次结构中(挂载)。
  • 错误处理(error handling)
    • 错误应该尽可能地在接近硬件的层面得到处理。
  • 同步(synchronous;即阻塞)和异步(asynchronous;即中断驱动)传输
  • 缓冲(buffering)
    • 数据离开一个设备之后通常并不能直接存取到其最终的目的地。
    • 例如网络进来一个数据包,要将该数据包存放在某个地方并进行检查,操作系统才知道要将其置于何地。
  • 共享设备和独占设备

2.2 实现I/O

I/O可以采用三种根本上不同的方式来实现

  • 程序控制I/O(programmed I/O)
    • 意思就是让CPU做全部工作
    • 实现简单,但是占用CPU时间
  • 中断驱动I/O
    • 进行I/O时CPU可以进行其他运算,准备好下一次IO操作时,产生一个中断。
    • 明显缺点是,例如打印机打印,中断会发生在每一个字符上,频繁中断浪费CPU时间。
  • DMA的I/O
    • 通过DMA给IO设备提供数据,而不需要打扰CPU。
    • 将中断次数从每个字符一次减少到每个缓冲区一次。

3. I/O 软件层次

IO软件通常组织成四个层次,每一层具有一个要执行的定义明确的功能和一个定义明确的与邻近层次的接口。

在这里插入图片描述

3.1 中断处理程序

当中断发生时,中断处理程序将做它必须要做的全部工作以便对中断进行处理。

以下这些步骤是硬件中断完成之后必须在软件中执行的

  • 保存没有被中断硬件保存的所有寄存器。
  • 为中断服务过程设置上下文。
  • 为中断服务过程设置堆栈。
  • 应答中断控制器,如果不存在集中的中断控制器,则再次开放中断。
  • 将寄存器从它们被保存的地方复制到进程表中。
  • 运行中断服务过程,从发出中断的设备控制器的寄存器提取信息
  • 选择下一次运行哪个进程。
  • 为下一次运行的进程设置MMU上下文。
  • 装入新进程的寄存器。
  • 开始运行新进程。

3.2 设备驱动程序

设备寄存器的数量和命令的性质在不同设备之间有着根本性的不同,因此连接到计算机上的IO设备都需要某些特定的代码来对其进行控制,这样的代码称为设备驱动程序(device driver)

在这里插入图片描述
不同的设备控制器虽然功能不同,但是设备驱动程序会提供统⼀的接⼝给操作系统,这样不同的设备驱动程序,就可以以相同的⽅式接⼊操作系统。

3.3 与设备无关的I/O软件

与设备无关的软件的基本功能是执行对所有设备公共的I/O功能,并且向用户层软件提供一个统一的接口。

  • 设备驱动程序的统一接口
    • 对于每一种设备类型,操作系统定义一组驱动程序必须支持的函数,操作系统通过调用该函数来对设备发起调用。
  • 设备命名
    • 与设备无关的软件要负责把符号化的设备名映射到适当的驱动程序上。
  • 设备保护
    • 系统给设备设置适当的访问权限,防止无权访问设备的用户访问设备。
  • 缓冲
    • 作用:
      • 缓和CPU和IO设备之间速度不匹配
      • 减少CPU的中断频率,放宽对CPU中断响应时间的限制
      • 解决数据粒度不匹配问题
      • 提高CPU域IO设备之间的并行性
    • 单缓冲:在内核空间创建一个缓冲区,在该缓冲区被填满之后,一次性将内核缓冲区的内容复制到用户缓冲区,然后唤醒用户进程执行CPU操作。
    • 双缓冲:因为单缓冲无法解决用户操作缓冲区的时候继续读入新的内容,所以引入第二个缓冲区,一个用于复制到用户空间,一个用于收集新的输入,实现双向通信。
    • 循环缓冲区:由一个内存区域,两个指针组成,一个指向下一个空闲的字,新的数据放到这,一个指向缓冲区数据的第一个字,从此处取走数据。
    • 缓冲池:将很多缓冲区放在池里,需要的时候从池里取。
  • 错误报告
    • 当错误发生的时候,操作系统必须尽最大的努力对它们进行处理,许多错误是设备特定的并且必须由适当的驱动程序来处理。
    • 一种是编程错误,一类是实际的IO错误。
  • 分配与释放专用设备
    • 某些设备任意时刻只能一个进程使用,所以相当于给该设备加锁,阻塞其他进程使用。
  • 与设备无关的块大小
    • 不同的磁盘可能具有不同的扇区大小,应该由与设备无关的软件来隐藏这一事实并且向高层提供一个统一的块大小。

3.4 用户空间的I/O软件

  • 一种用户层的IO软件是靠系统调用的库函数来组成,通过与设备无关的IO软件提供的接口来进行系统调用进行IO。
  • 另一种是假脱机系统。是多道程序设计系统中处理独占IO设备的一种方法,将独占式的设备改成共享式的设备。

4. 盘

盘具有多种多样的类型,最为常用的是磁盘,它们具有读写速度同样快的特点,这使得它们适合作为辅助存储器。

4.1 磁盘

  • 磁盘由表面涂有磁性物质的圆形盘片组成
  • 每个盘片被划分为一个个磁道,每个磁道又划分为一个个扇区

在这里插入图片描述

  • 磁盘有多个盘片"摞"起来,每个盘片有两个盘面
  • 所有盘面中相对位置相同的磁道组成柱面

在这里插入图片描述
在这里插入图片描述

4.2 磁盘臂调度算法

读或者写一个磁盘块需要多少时间,通常以以下三个因素决定

  • 寻道时间:将磁盘臂移动到适当的柱面所需的时间
  • 旋转延迟:等待适当扇区寻找到磁头下所需的时间
  • 实际数据传输时间:真正读写数据花费的时间

对大多数磁盘而言,寻道时间与另外两个时间相比占主导地位,所以减少平均寻道时间可以充分地改善系统性能。

  • 先来先服务(First-Come,First-Served,FCFS)
    • 按照接受顺序完成请求,先来的请求先处理
    • 缺点:很难优化寻道时间
  • 最短寻道时间优先(Shortest Seek First,SSF)
    • 优先选择从当前磁头位置所需寻道时间最短的请求。
    • 缺点:可能产生饥饿
  • 扫描(Scan)/ 电梯(elevator)算法
    • 磁头在⼀个⽅向上移动,访问所有未完成的请求,直到磁头到达该⽅向上的最后的磁道,才调换⽅向
    • 缺点:各个磁道的响应频率不平均,中间部分相⽐其他部分响应的频率会⽐较多
  • 循环扫描(Circular Scan, CSCAN )
    • 只有磁头朝某个特定⽅向移动时,才处理磁道访问请求,⽽返回时直接快速移动⾄最靠边缘的磁道,也就是复位磁头,这个过程是很快的,并且返回中途不处理任何请求.
    • 简而言之就是只朝一个方向移动,处理完一个方向之后回到起点扫描。
  • LOOK 与 C-LOOK算法
    • LOOK基于扫描算法的改进,如果该方向没有请求立刻调头。
    • C-LOOK基于循环扫描的改进,如果方向上没有请求立刻回到起点重新扫描。

在这里插入图片描述

5. 操作系统 IO 层次

如下是Linux 系统中一次读请求在核心空间中需要经过的层级架构。
在这里插入图片描述

  • 虚拟文件系统层

虚拟文件系统类似扮演一个文件系统的管理者,相关的数据结构都存在于内存中,作用是,屏蔽文件系统的差异,向上提供统一的接口,使得 Linux 中可以共存不同的文件系统并且对文件的操作可以跨文件系统而执行。

Linux 中主要通过四个数据结构来控制虚拟文件系统

	1. 超级块: 超级块对象表示一个文件系统,存储了文件系统的控制信息,包括名称文件系统的大小和状态、块设备的引用和元数据信息(比如空闲列表等等)。存放于磁盘中。
	2. 索引节点:索引结点对象存储了文件的相关元数据信息,一个文件会对应一个索引节点。
	3. 目录项:目录项的作用是方便文件的查找,只存在于内存中。
	4. 文件对象:文件对象描述的是进程已经打开的文件。因为一个文件可以被多个进程打开,所以一个文件可以存在多个文件对象。
  • 具体文件系统层

具体的文件系统大致分为三类:

1.磁盘文件系统
	这种文件系统用于管理磁盘中的文件,如Ext2/3,overlay等
2.网络文件系统
	这种文件系统可以使实现在本地访问远端的计算机的文件,如常见的NFS文件系统,SMB等
3.特殊文件系统
	这种文件系统不管理本地或者远端磁盘空间,不需要磁盘分配空间,但是占用内存,如proc文件系统,sysfs,sockfs等
  • Page Cache 层

引入Cache层的目的是为了提高Linux操作系统对磁盘访问的性能。Cache层在内存中缓存了磁盘上的部分数据。当数据的请求到达时,如果在Cache中存在该数据且是最新的,则直接将数据传递给用户程序,免除了对底层磁盘的操作,提高了性能。

Linux 文件的Cache分为两个层面。分别是 Page Cache 和 Buffer Cache ,一个 Page Cache 包含了多个 Buffer Cache。Page Cache主要用来作为文件系统上的文件数据的缓存来用,尤其是针对当进程对文件有read/write操作的时候。Buffer Cache则主要是设计用来在系统对块设备进行读写的时候,对块进行数据缓存的系统来使用。

磁盘 Cache 的两大作用就是:预读回写
对于预读,就是发挥局部性原理,在磁盘IO读取时,会顺带读取之后若干页面的内容。
对于回写,是会暂时将数据存在Cache里,然后统一异步写到磁盘中。

什么是局部性原理:
	时间局部性:一个被引用的信息,会在不久的未来被多次引用。
	空间局部性:一个被引用的信息,之后引用的信息大概率会在存储空间上与该信息相邻。
  • 通用块层

通用块层的主要工作是:接收上层发出的磁盘请求,并最终发出I/O请求。该层屏蔽底层硬件块设备的差异。

  • IO 调度层

I/O调度层的功能是管理块设备的请求队列。即接收通用块层发出的I/O请求,缓存请求并试图合并相邻的请求。并根据设置好的调度算法,回调驱动层提供的请求处理函数,以处理具体的I/O请求。通过一些方式,例如将随机IO转化为顺序IO等操作来提高IO效率。

  • 块设备驱动层 和 物理块设备层

也就是物理硬件设备和他接入操作系统的驱动程序。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值