Skip to content

持久化

物理存储

  • 希望更大、更多的数据能 “留下来” (并且被操作系统有效地管理起来)
  • 持久存储器本质上就是一个巨大的 bit/byte array,允许按照 block 读写

磁存储

  • 磁带 1 D
    • 价格低廉,容量较高,存在机械部件并且存在丢失风险可靠性一般
    • 勉强顺序读写,但是几乎不支持随机读写
    • 主要用于冷数据的存档和备份
  • image.png|500
  • image.png|500
    • 价格低容量高,较高数据读写性能,勉强随机读写
  • 软盘
    • 价格低,容量一般,可靠性低
    • 顺序读写随机读写都很慢

坑存储

  • 光盘:在反射平面上挖出粗糙的坑
    • 激光扫过表面,就能读出坑的信息来
    • 一种只读的存储介质
    • 成本极低(批量生产),容量可靠性较好
    • 顺序读取性能一般,随机读取很差
    • 主要用于数据分发,逐渐被高速网络替代

电存储

  • 去除机械部件,降低延迟
  • 价格低,容量极高,可靠性高,顺序读取极快,随机读取极快
  • 缺点:读写寿命差
  • image.png|600
  • 主控负责将数据均匀的写入到不同的块

    • Page (读取的最小单位, e.g., 4KB)
    • Block (擦除和写入的最小单位, e.g., 4MB)
    • 写时拷贝
  • NAND 闪存技术

    • 写入之前从事需要先擦除一大块内存,产生磨损和较大的时间代价
    • image.png|450
  • FLT 负责接收逻辑块上的读写请求,并将其转化为底层物理块和物理页上的低级读取、擦除和编程命令。
    • 使用直接(固定)映射效率较低,并且存在磨损寿命问题
    • 日志模式:保存一个映射表存储系统中逻辑块对应的物理地址。提升了性能(避免每次写入都擦除将随机写入转化为顺序写入),并且提供了磨损均衡的功能

文件管理

  • 数据项:文件系统中最低级数据组织形式:
    • 基本数据项:描述一个对象的某种属性的一个值(数据中最小逻辑单位)
    • 组合数据项:多个基本数据项组成
  • 记录:相关数据项的集合,描述对象在某方面的属性
  • 文件:具有文件名的一组相关元素的集合。即有名字的对象,是一个字节序列
    • 文件有一个 offset 指针,指向访问的位置(就不需要进程自己维护进度
    • read 和 write 都会自动维护 offset,lseek 则可以直接修改 offset 的位置
    • open 一个文件时创建一个新的 offset
    • 所有命令(cat 等)均是基于这一系列机制和系统调用来实现的
  • 文件描述符:指向操作系统对象的指针(操作系统对象的访问都需要指针)
  • 文件是以硬盘为载体存储在计算机上的信息集合
  • 文件系统用于实现对文件的维护管理

  • 文件控制块:操作系统通过文件控制块 FCB来维护文件元数据

    • FCB 的有序集合就是文件目录,一个 FCB 就是一个文件目录项(也可以被视为文件-目录文件)
    • 存储文件的基本信息(文件名、物理位置);基本控制信息(存取权限);使用信息(上次修改时间)
  • 目录结构:由于检索时可能不需要 FCB 完整信息,可以将文件名与文件描述信息分离,将文件描述信息单独为索引节点,文件目录中的每个目录项进包括文件名和索引节点号(这样就减少了单个条目的大小)

文件的基本操作

  • 创建文件:为新文件分配外存空间;在目录中创建目录项
  • 删除文件:根据文件名查找目录,删除对应的目录项和文件控制块然后回收文件占用的存储空间
  • 读写文件:先根据文件名查找目录,找到指定文件的目录项后从中得到文件在外存中的地址,之后利用目录项中的指针进行读写操作
  • 访问类型:读、写、执行(装入内存执行)、追加(append)、删除、列表清单(元数据)
  • 访问控制

    • 根据用户身份进行控制,对用户进行分类:拥有者、组内其他成员、其他
    • 3(用户类型)*4(读、写、删除、执行)位表示
  • 打开文件:

    • 多次读写一个文件时总需要检索目录,为了减少重复操作,系统维护一个包含所有打开文件信息的表(打开文件表)
    • open 打开文件后将目录项从外存复制到内存的打开文件表,并向用户返回文件描述符
    • 用户再次打开时可以直接用文件描述符从打开文件表获取信息(即只有初次打开才使用文件名)
    • close 后从打开文件表移除
  • 多进程文件
    • 多个进程可以同时打开文件,使用两级表来表示:
      • 系统表:包含与进程无关的信息,如文件在磁盘上的位置、大小等
      • 进程表:进程对文件的使用信息,如读写指针
    • 一个进程 open 时,在进程表中增加了一个条目,指向系统表的对应条目
    • 系统表会维护打开计数器,文件不再(计数器为 0)使用时自动从系统表删除
    • a2516ac49185dc6c9f19a90caec8187.jpg|500

文件结构

逻辑结构
  • 无结构文件:由字符串流组成(流式文件),通过读写指针以字节的形式进行访问(只能穷举搜索),源程序、可执行文件等都是。
  • 有结构文件:由一个以上的记录构成

    • 定长记录:文件中所有记录长度都相同,检索较快(随机访问)
    • 变长记录:长度不一定相同,只能顺序访问,较慢
  • 顺序文件

    • 文件中的记录按顺序排序,读写效率较高,但是查找、修改较慢
    • 串结构:记录之间的顺序取决于插入顺序,只能顺序查找
    • 顺序结构:按关键字排列,可以折半查找
  • 索引文件
    • 解决变长记录顺序文件不能随机访问的问题
    • 索引表包含指向记录的指针和记录长度,索引表本身是一个定长记录的顺序文件
  • 索引顺序文件
    • 每个索引指向的项为一组记录,通过索引找到组之后再顺序查找
    • 最佳划分 \(\sqrt{ n }\) 个索引,每个组 \(\sqrt{ n }\) 个条目,平均查找次数 \(\sqrt{ n }\)
    • 即分块查找
  • 直接文件/散列文件
物理结构
  • 连续分配

    • 每个文件在磁盘上占用连续的块
    • 支持顺序访问、直接访问
    • 速度较快,磁头移动距离少
    • 产生外部碎片,无法满足动态增长需求,不适合对文件进行增删
  • 链接分配

    • 消除了外部碎片,提高利用率
    • 便于动态增长即管理
    • 隐式分配
      • 用链表来表示,每个盘块有指向下一个盘块的指针
      • 问题:只支持顺序访问、存在稳定性问题(一个块损坏)
      • 可以按簇(多个连续块一组)进行分配,可以加速查找并且减少指针,但是会增加内部碎片
    • 显示链接
      • 用链接表表示块之间的链接关系,即文件分配表 FAT
      • 每个表项存储盘块号和下一块的地址
      • 用-1 表示最后一块,-2 表示空闲
      • 支持顺序访问(FAT 不大,可以加入内存,处理很快)减少了磁盘访问次数
      • 2a66e41ea779b3269d5d0196a65d940.jpg|500
  • 索引分配

    • 单级索引
      • 将每个文件的盘块号放在一起,访问时调入内存,而不需要将整个 FAT 放在内存
      • 索引也作为一个块在磁盘上
      • 问题时索引块占用空间,文件很大时还需要很多索引块链接
      • |500
    • 多级索引
      • 处理索引块太多时的问题
      • 问题:需要加载很多索引块,IO 开销较大
    • 混合索引
      • 兼顾大中小文件,假设一个块 \(4KB\)
      • 对于小文件直接将每个盘块地址直接放入 FCB,直接寻址访问;比如设置 10 个直接地址(最多有 10 个块)即支持 \(40KB\)
      • 对于中型文件使用单级索引,在 FCB 指向索引表,一次间址 (一个索引块里 1024 个盘块),即同时使用支持 \(4MB+40KB\)
      • 对于大文件使用多级索引,如同时使用到三级,就支持 \(4TB+4GB+4MB+40KB\)
      • 63fa240764a1798b849315b41f073d8.jpg|500

目录

  • FCB 作为文件目录项
  • 目录提供了文件名到文件的映射(按名存取)

  • 单级目录:线性目录表,通过遍历查找,并且不允许重名

  • 两级目录:分为主文件目录(记录用户名和相应的用户目录文件的位置)和用户目录(用户所有文件的 FCB)多用户之间可以存在同名文件。但是还不够灵活
  • 树形目录:提高目录的检索速度和文件系统的性能,通过文件路径名来访问文件,系统中每一个文件都有唯一的路径(大多操作系统现在都是使用树形目录)。为了避免反复查询,可以添加一个当前工作目录,使用相对路径进行访问(但是非初次访问还是使用文件描述符)
    • 层次结构清晰,便于分类管理,但是查询相对较慢,并且有较多磁盘 IO
  • 无环图目录:在树形目录的基础上加上一些指向同一节点的有向边,得到一个 DAG,可以更方便的共享目录、文件。

    • 在想要删除节点时同样可以使用访问计数来管理
  • 文件共享:多个用户共享一个文件时,系统中只需保留该文件的一个副本

    • 硬链接:基于索引节点,两个用户的文件目录指向相同的共享文件的索引节点指针
      • |500
    • 软链接:利用符号链实现文件共享,创建一个 LINK 类型的新文件,指向要共享的文件(类似于快捷方式)操作系统遇到 LINK 文件后会重定位使用其中记录的路径去查询文件 F
      • image.png|500
      • 当文件所有者删除一个文件之后,其他用户通过 LINK 进行访问就会出错
  • 目录的查询方式:线性法、查询法

文件系统

  • 如何构建文件系统?

    • 信息具有局限性:在一个树状目录中逻辑相关的数据存放在相近的目录
    • 对于"非数据"文件:unix 中一切都是文件都在'/'中;windows 中一个驱动器一个文件树
  • image.png|150

  • 文件系统布局

    • 文件系统在磁盘中的结构:磁盘每个分区有一个独立的文件系统
      • 主引导记录 MBR:位于磁盘 0 号扇区,MBR 包含主引导程序和分区表(给出每个分区的起始和结束地址),通过 MBR 来确认活动分区,并读入对应的引导块
      • 引导块:MBR 执行引导块中的程序之后负责启动该分区中的操作系统
      • 超级块:包含文件系统的所有关键信息,在计算机启动时加入内存,包含:分区快的数目、大小、空闲快、空闲 FCB 等
      • image.png|500
    • 文件系统在内存中的结构:用于管理文件系统并通过缓存来提高性能
      • 安装表:安装的文件系统的分区的相关信息
      • 目录结构和缓存:包含最近访问的目录信息
      • 整个系统代开文件表
      • 每个进程打开文件表记文件描述符(句柄)
  • 外存空闲空间的管理

    • 一个磁盘可以进行分区(卷),每个分区包含目录区FCB和文件区;多个磁盘也可以通过 RAID 组成一个分区
    • 存储设备管理实际上就是对外存中的空闲块进行组织和管理
    • 空闲表法:
      • 属于连续分配方式,类似于内存的动态分区分配,每个文件分配一段连续的存储空间
      • 每个空闲区对应一个空闲表项:包含序号、起始块号和数目
      • 分配上也可以使用首次、最佳适应等短发
      • 回收时同样考虑进行区间合并
      • 具有较高的分配速度
    • 空闲链表法:
      • 把所有空闲盘区组织为一个空闲链
      • 空闲盘块链:一盘块为单位拉成一条链;用户请求时从开始位置选取一定数目空闲狂进行分配;释放时将盘块插入到末尾;效率较低(单位太小了)
      • 空闲盘区链:将空闲盘区拉成链(每个盘区包含若干相邻的盘块),类似于动态分区分配,效率相对较高但是比较复杂
    • 位示图法:
      • 用二进制每一位表示磁盘中盘块的使用情况
      • 如 m 个 n 位数表示 \(m\times n\) 个盘块的使用情况
    • 成组链接法:
      • 更适用于大型文件系统
      • 将空闲盘块分组,如 100 个一组,每组第一个盘块记录下一组的数目和空闲盘块号,即由各组的第一块连接成一条链
      • image.png|500
      • 分配:移动指针进行分配,移动到栈底时切换到下一个分组继续分配
      • 回收:将回收的盘块号放入栈顶并移动指针,已满时创建新栈
  • 虚拟文件系统

    • 屏蔽差异,为用户提供统一接口
    • image.png|500
  • 文件系统挂载
    • 文件系统在使用之前需要先进行挂载(之后能在统一的文件系统树中进行访问,这就是目录树的拼接)
    • 挂载到目录之后就可以通过目录访问设备上的文件
    • 可以将不同的文件系统挂在到一棵文件系统树上
    • 使用:超级块对象;索引节点对象;目录项对象;文件对象;
    • 挂载镜像(虚拟磁盘),会自动创建一个新设备(loopback 设备),与镜像内容绑定

文件系统的实现

  • 除了文件具体数据内容外,文件系统还需要存储文件的元数据信息,通常存储在 inode 结构中,专门存储在文件系统的一块固定区域。此外还需要位图(bit 表示块是否空闲),使用位图记录 inode 和数据区是否空闲。最后还需要超级快来存储一些文件系统相关的信息,如文件系统的组织形式等。
  • image.png|500
  • inode 描述的是单个文件或目录的元数据,而超级块描述的是整个文件系统的全局元数据

  • 文件组织:inode(索引节点)

    • image.png|500
    • 对于较小的文件可以直接在 inode 中存储一个或多个指针分别指向属于该文件的磁盘块,对于较大文件使用多级索引
  • 读取操作:不涉及块的分配,不需要使用 bitmap
    • 首先获取根目录的 inode 然后递归遍历到所需的 inode,加载到内存并分配文件描述符
    • 程序通过文件描述符来访问文件
    • 不再使用时文件描述符被释放
  • 写入操作:设计空闲块的分配,涉及到对 bitmap、inode 和 data 的读写

  • 大多数文件系统会使用系统内存来缓存来减少对文件的 IO 操作,通过批处理加速/避免不必要的操作

FAT 文件系统
  • 物理存储设备带来的问题:本身并不能真正的随机读取,每次读取写入的都是一个固定大小的块或页
    • 问题:读写放大,被迫读写连续的一块数据
    • 解决方式:局部性+缓存
  • 目录中一般只有十几个文件并且以小文件为主
  • 文件的实现:链表表示文件所用的块
    • image.png|500
    • 集中存储(next 数组)性能更好,通过集中存放多份解决安全性问题
  • 目录的实现也是普通文件,操作系统根据文件内容对目录解读,使用一个线性表(数组)进行存储
  • 快速格式化=FAT 表丢失,但是数据还在
  • 适合小文件存储,大文件的随机访问性能较差
ext/unix 文件系统
  • 为大小文件区分,小文件使用数组,大文件使用平衡树
  • image.png|500
快速文件系统 FSS
  • 性能优化:尽可能多顺序存储,在存储文件时也要注意存储布局,避免过于碎片化。
  • FSS 通过优化磁盘访问模式和减少磁盘寻道时间来提高文件系统的性能。如 FSS 会将文件的数据块尽量连续地存储在磁盘上,以减少磁盘头的移动次数。
  • FSS 通过更有效的空间分配策略来提高磁盘空间的利用率。例如,使用较小的块大小来减少内部碎片,并通过延迟分配和灵活的 inode 管理来优化空间使用。
  • 此外还提升了可靠性、一致性(日志机制、冗余机制)、扩展性、灵活性等

  • 引入柱面组,每个柱面分别存储 inode、data 等信息而不是集中存储(这样同一文件的 inode 和 data 就位于同一个柱面)。这样 inode 和 data 直接交替访问(这是很常见的)就不需要移动很远的距离,可以提高访问的局部性和效率

  • 新目录优先分配到空闲空间较大的 inode,同一目录下的文件尽可能分配在同一柱面,同一文件更是,由此提高局部性
  • 大文件会占据整个块组,妨碍了随后的“相关”文件放置在该块组内,因此可能破坏文件访问的局部性。
    • 只要平衡块的大小(大文件每个部分占据块的比例)就仍然能保证大部分时间用于有效传输的较高效率
    • image.png|500
日志文件系统 LFS
  • 瓶颈主要在于写入性能
  • 写入磁盘时,LFS 首先将所有更新缓冲在内存段中。当段已满时,它会在一次长时间的顺序传输中写入磁盘,并传输到磁盘的未使用部分。LFS 总是写入磁盘的未使用部分,然后通过清理回 收旧空间,而不是在原来的位置覆盖文件。

  • 顺序写入:不仅仅是 data,需要将所有更新(包含 inode)等全部连续顺序地写入到磁盘

    • LFS 会跟踪内存中的更新,收到足够数量(足够大的连续段)的更新时,会立即将它们写入磁盘,从而确保有效使用磁盘
    • image.png|500
  • LFS 中的 inode 不再连续分布

    • 使用 inode 映射:在 inode 之间引入间接层:imap 以 inode 作为输入并生成最新版本的 inode 的磁盘地址。每次 inode 写入磁盘是,imap 都会进行更新。
    • imap 放置在其他信息的位置旁边(inode 旁边)
      • image.png|500
    • 磁盘上有一个固定的位置,检查点区域 CR 包含了指向 imap 的指针,其定期更新(如 30 s)构成 CR-imap-inode-data 的层次结构
    • imap 记录了每个 inode 号到其在磁盘上的最新位置的映射。每次 inode 被修改后,其新位置会被记录在日志中,imap 也会随之更新,指向最新的 inode 位置。当文件系统需要访问某个 inode 时,会首先通过 imap 找到 inode 的位置。(这也避免修改目录时,一个文件该表造成递归全部需要修改,imap 的地址不变就阻断了这种现象)imap 是可以修改的
  • 垃圾清理:由于总是追加写入,磁盘中存在很多已经失效的磁盘碎片

    • LFS 清理程序按段工作,从而为后续写入清理出大块空间:FS 清理程序定期读入许多旧的(部分使用的)段,确定哪些块在这些段中存在,然后写出一组新的段,只包含其中活着的块,从而释放旧块用于写入。
    • LFS 为描述每个块的每个段添加一些额外信息:包括其 inode 号(它属于哪个文件)及其偏移量,通过段摘要块找到对应的块并进行对比,从而判断是否是已经过时的“垃圾块”
    • 更好的方法:当文件被截断或删除时,LFS 会增加其版本号,并在 imap 中记录新版本号。通过在磁盘上的段中记录版本号,LFS 可以简单地通过将磁盘版本号与 imap 中的版本号进行比较,跳过上述较长的检查,从而避免额外的读取

可靠存储

  • 临时失效:断电
  • 部分失效:ECC 纠错失效
  • 永久失效:物理损坏等
RAID 可靠磁盘
  • 把多个磁盘虚拟成一块非常可靠并性能极高的虚拟磁盘
  • 从虚拟块号到磁盘物理块号的映射
  • image.png|500
  • image.png|500
  • 大文件访问涉及多个块可以充分利用不同磁盘同时读取带来的加速
  • 小问价只涉及单独的块,无法利用 RAID 带来的性能上的提升
  • image.png|600
  • image.png|600
可靠文件系统
  • 一次写入可能分为多个步骤,如修改 FAT 块、修改数据块等,此时发生断电就会出现问题。即对磁盘的操作不是一个原子操作
    • 三个写入:inode、bitmap、data
  • 尤其是磁盘是延迟写入的,高速操作系统已经写入时并不一定完成了物理写入,存在崩溃一致性问题

  • FSCK:根据磁盘上已有的信息,恢复出 “最可能” 的数据结构,一种文件检查系统

  • 文件系统的检查方法比基于日志的检查方法得多(日志大小小的多)
  • 假设如下一个追加数据的写入场景
    • image.png|500
    • image.png|500
    • 涉及到对 inode、bitmap 和 data 的写入,如果写入部分完成时发生崩溃,就会出现问题(比如 inode 说已经完成了但是 data 实际上没有写入,或者 bitmap 没有被更新导致之后的覆盖问题)
    • 由此崩溃就导致了不一致的问题,我们希望将文件系统保证从一个一致状态转移到另一个一致状态,这就是崩溃一致性问题
  • 文件系统检查:fask 就用于在出现故障之后重新启动时查找不一致并进行修复

    • 检查超级块、空闲块、inode 状态等进行检查识别可疑的问题并进行修复。
    • 由于需要扫描整个磁盘,十分耗时。
  • 磁盘的三种操作:

    • bwrite、bread:为了性能优化,没有顺序保证,并且由于缓存也不一定真正存进了磁盘
    • bflush:等待已写入的数据落盘(真正存储到了磁盘)
  • 预写日志检查
    • 使用 appendonly 的方式记录所有历史操作
    • 通过重新执行所有操作得到数据结构的当前状态
    • 很容易实现崩溃的一致性
    • 结合数据结构(文件系统的直观表示)和日志系统就能实现崩溃一致性。高效读写+可靠机制。
    • image.png|500
    • TxBE 表示事务的开始和结束,中间存储的是具体要进行的操作
    • 由于缓存机制,磁盘可能不会按顺序写入事务的各个部分(比如 TxE 完成但是 Db 没有正确写入,这就会造成问题)。因此分两步发出事务写入:首先将除了 TxE 之外的块写入,完成之后再发出 TxE 的写入
    • 日志写入:将事务的内容(包括 TxB、元数据和数据)写入日志,等待这些写入完成。
    • 日志提交:将事务提交块(包括 TxE)写入日志,等待写完成,事务被认为已提交
    • 加检查点:将更新内容(元数据和数据)写入其最终的磁盘位置。
    • 释放日志:由此可以循环利用日志空间
  • 使用日志从崩溃中恢复

    • 如果事务还没有提交:跳过待执行的更新,重新进行操作
    • 事务已经提交:扫描日志,根据日志检查已经完成的事务,事务被重放,文件系统再次尝试将事务中的块写入它们最终的磁盘位置。即通过日志恢复已经提交的事务
  • 优化

  • 元数据日志:避免重复写入两次数据,对于数据部分直接先写入到目标位置,仅在日志中记录元数据
    • 在日志中存储要写入数据的完整信息的称为完整数据日志
    • image.png|550
  • 批处理:多次系统调用合并为一个事务,减少日志记录的大小和频率
  • 校验和,用校验和验证日志的完整性,这就不再标记事务开始(TxBegin)和事务结束(TxEnd)。

IO

IO 设备

  • IO 设备就是一个能与 CPU 交换数据的接口/控制器

    • 使用几组约定好功能的线,通过握手信号从线上(寄存器中)读出/写入数据
    • 可以给设备寄存器赋予内存地址,使得 CPU 可以直接使用指令和设备交换数据
    • image.png|396
    • 状态寄存器用于读取并查看设备的当前状态
    • 命令寄存器用于通知设备执行某个任务
    • 数据寄存器用于传给设备或从设备接受数据
    • 通常的工作流程:查看状态寄存器等待就绪状态;下发数据到数据寄存器;将命令写入命令寄存器;设备开始执行命令;操作系统检查设备等待并判断设备是否执行完成命令(或者使用中断方式以及 DMA)
  • 设备分类:

    • 独占设备:同一时刻只能由一个进程占用的设备,如低速设备(打印机)
    • 共享设备:同一时间段之内允许多个进程同时访问(并不是同时访问)
    • 虚拟设备:如 SPOOLing 技术将独占设备转化为共享设备,一个物理设别转化为多个逻辑设备
  • 为了让计算机可以不只连接到固定的设备,引入特殊的 IO 设备:总线

    • 提供设备的虚拟化:注册、转发
    • 把收到的地址 (总线地址) 和数据转发到相应的设备上,这样 CPU 只需要直连这一个设备就可以了
    • CPU 只负责和总线交互,总线负责将数据传递给 io 设备进行地址映射等任务
    • 通过总线简化了设备的管理,实现了管理更多不同的 IO 设备
    • image.png|400
  • 中断控制器

    • 负责收集各个设备产生的中断,并选择发送给 CPU(根据优先级等)
    • 以及完成对设备的应答
  • DMA
    • 负责进行数据的拷贝,解放 CPU 算力
    • 位移的功能就是 memorycopy
  • GPU

    • 专门画图的"CPU"做大量重复简单工作
  • 设备交互方式

    • 通过特定的特权指令与设备进行交互
    • 通过内存映射 IO 的方式进行交互

设备驱动程序

  • 把系统调用 “翻译” 成与设备能听懂的数据

IO 层次结构

以下是 I/O 层次结构及每一层特点和功能的表格:

层次 特点 功能
用户层(User Level) - 用户层是程序和操作系统的接口。
- 用户应用程序通过系统调用请求 I/O 操作。
- 提供易于使用的 I/O 操作接口,例如文件读写、网络通信等。
- 将用户的 I/O 请求传递给操作系统内核。
系统调用层(System Call Interface) - 是用户层和操作系统内核之间的桥梁。
- 提供一组标准的系统调用,用于执行各种 I/O 操作。
- 接收来自用户层的 I/O 请求并进行验证。
- 将经过验证的请求传递给内核进行处理。
设备无关软件层(Device-Independent Software) - 提供与具体硬件设备无关的 I/O 操作。
- 处理设备独立的 I/O 管理任务。
- 缓冲区管理:实现数据的临时存储和传输,减小 I/O 操作对 CPU 的影响。
- 缓存管理:提高 I/O 操作的效率,通过缓存减少对设备的直接访问。
- 设备分配:管理系统中可用的设备资源,处理设备的分配和释放。(逻辑设备与物理设备的映射)
- 错误处理:处理 I/O 操作中的错误,确保系统稳定性。
设备驱动层(Device Drivers) - 是操作系统内核的一部分,专门负责与具体硬件设备的交互。
- 每种设备都有相应的设备驱动程序。
- 将设备无关的软件层的 I/O 请求转换为设备特定的操作。
- 与硬件设备进行通信,控制设备的操作。
- 处理设备的中断请求,确保 I/O 操作的正确完成。
中断处理层(Interrupt Handlers) - 负责处理来自硬件设备的中断信号。
- 在硬件设备完成某个 I/O 操作时,触发中断处理。
- 保存当前 CPU 的状态,确保中断处理后系统能继续正常运行。
- 执行相应的中断处理程序,响应设备的请求。
- 恢复 CPU 状态,返回到中断前的执行状态。
硬件层(Hardware Level) - 包含所有的物理 I/O 设备,如磁盘驱动器、网络接口卡、显示器等。
- 直接与设备驱动层进行交互。
- 执行具体的 I/O 操作,例如数据的读写、数据传输等。
- 通过中断机制通知操作系统 I/O 操作的完成情况。

设备无关软件

  • 负责执行所有设备的公有操作
高速缓冲
  • 利用内存来暂存磁盘中的信息,逻辑上属于磁盘,物理上属于内存
  • 缓冲区同时只支持单向数据流
    • image.png|500
  • 单缓冲:每当用户发出一个 io 请求,就分配一个缓冲区(一个块)设备现将数据传送到缓冲区在传送到工作区
    • image.png|500
    • T 和 C 可以并行,处理每块数据的平均时间 \(Max(C,T)+M\)
  • 双缓冲: 设备输入时可以交替使用两个缓冲区
    • image.png|500
    • T 与 C、M 都可以并行 \(Max(T,C+M)\)
  • 循环缓冲:
    • 包含许多大小相等的缓冲区,in 指向第一个可以输入数据(缓存)的块;out 指向第一个可以提取数据(进行运算)的块
    • image.png|300
  • 缓冲池:管理多个缓冲区
    • 将缓冲区划分为:空缓冲队列;输入队列;输出队列
    • 收容输入数据的工作缓冲区 hin:从空缓冲区队列取下缓冲区作为收容输入的工作缓冲区,装满数据后挂到输入队列的队尾
    • 提取输入数据的工作缓冲区 sin:从输入队列队首取得缓冲区,作为提取输入工作缓冲区,使用数据后将缓冲区归还到空队列
    • 收容输出数据的工作缓冲区 hout
    • 提取输出数据的工作缓冲区 sout
设备分配与回收
  • 根据用户的 IO 请求分配所需的设备,尽可能让设备忙碌,又要避免造成进程死锁
  • 设备控制表 DCT:每个设备一张,表项为控制器的属性(如设备类型、标识符、状态、指针等)
  • 控制器控制表 COCT:每个设备控制器一张,每个控制器由一个通道控制,有指针指向
  • 通道控制表 CHCT:每个通道一张表,一个通道可以为多个控制器服务,可以通过表项查询
  • 系统控制表 SDT:整个系统一张,记录所有已链接到系统的物理设备的情况

  • 设备分配算法:FCFS(FIFO);最高优先级优先

  • 设备分配的基本过程:
    • 查找 SDT,寻找目标设备的 DCT,若忙则将进程 PCB 挂载到设备等待队列;否则分配设备
    • 分配控制器,根据 DCT 找到 COCT 同样若忙则加入控制器等待队列;否则分配
    • 分配通道;
    • 只有设备、控制器、通道全部被正确分配才算分配成功
  • 为了实现设备的独立性,使用逻辑设备名替代物理名,通过配置逻辑设备表 LUT 实现转换(逻辑设备名、物理设备名、驱动程序入口);也可以为用户也配备 LUT 表项指向系统的 LUT
SPOOLing 技术
  • 假脱机技术,将独占设备改造成共享设备
  • 通过将数据暂存到中间存储介质(如磁盘或内存)中,使得计算机系统可以同时处理多个输入输出任务,从而提高系统的效率和性能。
    • 需要输入时直接从磁盘读取
    • 需要输出时也是先将数据存储到磁盘
  • 4f0a92092836b0c9ffd798f6428c85f.jpg|500
  • 输入输出井:模拟脱机输入输出的磁盘,用于收容数据
  • 输入缓冲区和输出缓冲区:用于在内存暂存数据
  • 输入输出进程:输入进程将用户数据从输入设备到缓冲区在存储在输入井,CPU 需要时再取出;输出进程将用户的数据从内存传输到输出井,等设备空闲时再交给设备
  • 井管理程序:控制作业与磁盘之间的信息交互

  • 提高速度:将低速 IO 操作转化为对磁盘缓冲区中数据的操作;将独占设备改造为了共享设备;实现了虚拟设备功能