操作系统中的磁盘
引言
本文主要讲解操作系统管理磁盘的原理,概念等。请先回答如下问题,带着问题阅读文章效果更佳。
- 操作系统是如何管理磁盘的?
- 如何能提高磁盘的访问速度?
- 当多个线程同时请求读取操作系统是如何调度?
- 文件系统是如何组织的?
以下是笔者自己的博客,持续分享java技术栈相关的知识。博客链接:
https://www.yuque.com/objectn/gx4owr
认识磁盘


读写磁盘
读写磁盘的基本过程,首先移动磁头移动到磁道上,然后转动磁道到对应的扇区上。 可简化为:控制器->寻道->旋转->传输。读写磁盘需要的数据:柱面( C )、磁头( H )、扇区( S )、缓存(内存位置)。把上述这几个值写到磁盘寄存器上。
为了使用户更方便的使用磁盘和提高磁盘读写效率做了以下抽象:盘块号,
通过盘块号读写磁盘
用户直接使用上面的几个参数比较麻烦。对此进行抽象,通过一个盘块号(block)去找到对应的位置。操作系统通过盘块号加工出柱面、磁头、扇区,进而找到对应的位置。
提高读写速度(盘块的抽象)
先来看一下磁盘的访问时间构成:
磁盘访问时间= 写入控制器时间+ 寻道时间+ 旋转时间+ 传输时间。其中最耗时的就是寻道时间。就是磁头到找到对应扇区的时间。因此连续的内容尽量要写在同一个磁道上。
通过一次多读取几个扇区来提高读写效率,连续的几个扇区也叫盘块。但是多读取几个扇区也有空间上的浪费。浪费的原因:最小分配单位是“盘块”,不是“字节”:操作系统为了管理方便,不会按字节给文件分配磁盘空间,而是以“盘块”为单位。一个盘块通常由多个连续扇区组成(例如 4KB = 8 个 512 字节扇区)。即使一个文件只有 1 字节,它也必须占用整整一个盘块。
下面是空间利用率和读写速度的关系图:

磁盘调度算法

由于操作系统使多进程的,为了支持多进程引入了请求队列,这样如何调度就成了问题。就有了磁盘调度算法,核心还是是要减少寻道时间。
简单解释以下磁盘调度的过程
- 进程通过盘块号,计算出扇区号
- 通过电梯算法得到放入把盘块号加入请求队列中。
- 然后根据磁盘驱动计算出s,h,c
- 磁盘根据s,h,c得到数据,并给线程。
磁盘调度算法:
FCFS磁盘调度算法
- 原理:严格按照进程请求访问磁盘的先后顺序进行调度,不管磁头当前位置在哪里。
- 演示:
-
- 路径:53 → 98 → 183 → 37 → 122 → 14 → 124 → 65 → 67
- 优点:公平,不会出现“饥饿”现象(即某个请求永远得不到服务)。
- 缺点:平均寻道时间长,性能较差。如果请求序列跨度大(如从14跳到183再跳回37),磁头会剧烈摆动,效率极低。
SSTF磁盘调度
这是一种贪婪算法,只看眼前利益。
- 原理:每次选择距离当前磁头位置最近的那个磁道进行访问。
- 演示:
-
- 当前在53。
- 找最近的:65 (距离12) → 67 (距离2) → 37 (距离30) → 14 (距离23) → 98 (距离84) ...
- 路径:53 → 65 → 67 → 37 → 14 → 98 → 122 → 124 → 183
- 优点:比FCFS有较好的寻道性能,平均寻道时间较短。
- 缺点:可能导致“饥饿”。如果一直有请求集中在磁头附近(例如一直在60左右),那么远处的请求(如183或14)可能永远得不到服务。
SCAN磁盘调度
模拟了现实生活中的电梯运行逻辑。
- 原理:磁头沿一个方向(例如向磁道号增加的方向)移动,途中处理所有请求,直到到达磁盘的最边缘(即使边缘没有请求也要走到头),然后改变方向,反向处理请求。
- 演示(假设向右/大号方向移动):
-
- 路径:53 → 65 → 67 → 98 → 122 → 124 → 183 → (碰到边缘199掉头) → 37 → 14
- 优点:避免了SSTF的饥饿问题,吞吐量较高。
- 缺点:对刚扫描过的区域(例如刚刚走过的53左边的区域)不公平,这些区域的请求必须等到磁头走个来回才能被响应,导致等待时间方差较大。
C-SCAN磁盘调度(电梯算法)
这是对SCAN算法的改进,旨在提供更均匀的等待时间。
- 原理:磁头单向移动(例如只从左向右)。当磁头到达最边缘(199)后,立即快速返回到起始端(0),且在返回途中不处理任何请求。回到起点后,再次从头开始向右扫描。这就形成了一个环状结构。
- 演示:
-
- 路径:53 → 65 → 67 → 98 → 122 → 124 → 183 → (直接飞回0) → 14 → 37
- 优点:所有磁道的请求等待时间更加均匀,消除了SCAN算法两端快、中间慢(或反之)的差异。
- 缺点:由于回程时不服务,理论上单位时间内的服务次数略少于SCAN,但在实际系统中,因为减少了磁头频繁换向的机械损耗,往往表现更好。
总结对比表
表格
|
算法 |
核心策略 |
优点 |
缺点 |
适用场景 |
|
FCFS |
按顺序排队 |
简单、公平 |
效率低,磁头乱跑 |
负载极轻的系统 |
|
SSTF |
谁近选谁 |
平均寻道短 |
远处请求易“饥饿” |
对响应时间敏感 |
|
SCAN |
走到黑再回头 |
较好性能,无饥饿 |
两端请求等待久 |
通用系统 |
|
C-SCAN |
单向循环,回程不服务 |
等待时间均匀 |
逻辑稍复杂 |
数据库等重负载系统 |
从生磁盘到文件
盘块号不够直观,使用还是不方便,因此需要在抽象出一个概念文件。那么如何从文件得到盘块号?要建立字符流和盘块的映射关系。操作系统负责维护这个映射。
FCB(文件控制块):这是一个逻辑概念,泛指描述文件属性的所有信息集合。每个文件必然有且仅有一个完整的 FCB。这里记录了上面说的映射关系。
连续结构的映射表
实际上就是数组,访问比较快但是插入较慢。
- 做法:文件的所有盘块在磁盘上挨着放。文件中的FCB 回记录 文件名,起始块,块数。
- 缺点:极容易产生外部碎片,且文件很难动态扩展(如果文件后面紧挨着别的文件,就没法变长了)。


链式结构的映射表
是链表,插入快但是访问比较慢。
- 做法:文件可以分散存放在磁盘的任何地方。每个盘块里除了存数据,还留出一部分空间存“下一个盘块的物理地址”(指针)。文件中的FCB 会记录起始块。
- 缺点:
-
- 不支持随机访问:想读文件的第 1000 个盘块,必须从第 1 个开始,顺着链表数 1000 次,极其缓慢。
- 可靠性差:链表中只要有一个指针坏了,后面的数据就全丢了。


索引结构的映射表
- 做法:专门拿出一个盘块作为“索引块”,里面密密麻麻写满了数据盘块的物理地址。文件目录项(FCB)里只存这个索引块的地址。
- 优点:完美支持随机访问,直接查表即可
如果文件较大要使用多级索引。小文件直接映射数据块,中等文件一阶映射,大文件二阶映射。如下图说示:

目录与文件文件系统
引言
多个文件在磁盘中是如何组织?
操作系统开始时把所有的文件都放在同一个目录下,但是造成文件组织混乱,查找不方便。这就引出了目录这个概念,表示一个文件的集合。把不同分类的文件放在不同的目录下。这个目录是以树形结构组织的,方便查找。
下面问题的关键是如何实现目录到盘块的映射的。更具体的就是更具一个路径("my/data/a"),找到对应文件的FCB。
目录的实现
下图是一个目录树的示例:

如何把子文件的信息存储到父文件。可以把子文件的FCB存储到父文件中,但是FCB太大了,如果子文件较多,就会占用很多空间。要检索文件名,我们可以存储文件名和对应FCB对应的地址。这样就能节省很多空间。下图是文件树的组织结构。

系统实现自举,磁盘存储信息
首先解释一下自举,就是计算机“自己把自己拉起来”的过程。
实现自举磁盘的结构:

inode位图: 哪些inode空闲,哪些被占用数据区
盘块位图: 哪些盘块是空闲的,硬盘大小不同这个位 图的大小也不同
空闲位图(位向量)… 0011110011101 表示磁盘块2、3、4、5、 8、9、10、12空闲
超级块:记录两个位图有多大等信息。
根目录的节点编号是国定的,通常放在超级块中。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)