封面

🔥个人主页:爱和冰阔乐
📚专栏传送门:《数据结构与算法》C++
🐶学习方向:C++方向学习爱好者
⭐人生格言:得知坦然 ,失之淡然

在这里插入图片描述


🏠博主简介
在这里插入图片描述

前言

前面学习基础 I/O 时,我们主要研究的是“已经被打开的文件”:进程执行 open/fopen 后,内核怎样创建 struct file,再通过文件描述符 fd 找到这个打开文件对象。

但这篇开始,视角要往前挪一步:

文件还没有被打开时,它到底放在磁盘的哪里?操作系统又是怎么把成千上万个文件组织起来的?

一个文件可以先粗略看成:

核心认知: 文件 = 内容 + 属性

已经被打开的文件在内存里研究,未被打开的文件主要在磁盘上研究。用户想访问文件,必须能通过路径找到它;而“谁来组织文件、谁来查找文件、谁来把路径变成磁盘上的数据位置”,这就是文件系统要解决的问题。

这一篇先不急着讲 inode 和 Ext 的块组结构,而是先把磁盘本身讲清楚:磁盘为什么慢、扇区是什么、CHS 怎么定位、LBA 为什么能把磁盘抽象成一个一维数组,最后再引出文件系统为什么要使用“块”和“分区”。


一、先把文件分成两种状态

从宏观上看,文件可以分成两类:

文件状态 主要研究位置 之前/本文关注点
已经被打开的文件 内存 基础 I/O:fdstruct file、重定向、缓冲区
没有被打开的文件 磁盘 本文:磁盘结构、文件系统、inode、路径解析

之前基础 I/O 讨论的是:文件被进程打开后,进程如何通过 fd 找到内核中的打开文件对象。

而现在要讨论的是:文件还躺在磁盘上时,操作系统如何管理它们。

磁盘上的文件数量可能非常多,如果没有一套组织方式,用户根本找不到自己要的文件。我们平时看到的目录树、路径、分区、挂载,本质上都是文件系统为了管理磁盘文件做出来的抽象。


二、先理解硬件:磁盘为什么慢但容量大?

机械磁盘是计算机里比较典型的机械设备,它属于外设,特点很明显:

  • 速度慢,远慢于 CPU 和内存;
  • 容量大,价格便宜;
  • 适合长期保存数据;
  • 企业场景里常常会根据冷热数据选择不同磁盘。

比如很久以前的聊天记录、朋友圈、备份数据,通常不会放在非常昂贵的高速存储上,而是会放到容量大、成本低的存储设备里。最近访问的数据放在更快的位置,历史数据放在更便宜的位置,这就是常见的冷热数据分离。

机械硬盘外观

磁盘拆开后,能看到几个重要结构:

  • 盘片:真正存储数据的地方;
  • 主轴/马达:带动盘片高速旋转;
  • 磁头:负责读写数据;
  • 磁头臂/传动臂:带动磁头在半径方向上移动。

服务器、机柜和机房

服务器一般不需要像个人电脑一样直接接鼠标、键盘、显示器。程序员通常通过网络登录服务器,服务器更多依赖网卡对外提供服务。很多服务器放在一起,就组成了机柜;很多机柜放在一起,就是机房。


三、磁盘如何表示 0 和 1?

磁盘盘面看起来很光滑,但可以把它想象成由很多微小磁性颗粒组成。每个颗粒可以有不同的磁化方向,比如我们可以粗略规定:

可以这样理解:

  • 某个磁化方向表示 1
  • 另一个磁化方向表示 0

所以,向磁盘写入数据,本质上就是改变这些磁性颗粒的状态。

磁铁的南北极可以帮助理解磁盘记录 0 和 1

不同硬件表示二进制的方式不同:

设备 表示 0/1 的方式
磁盘 磁性颗粒方向变化
内存 电容充放电、电平高低
网络 电信号/光信号/波形变化

所以计算机底层统一认识的是二进制,但不同设备承载二进制的方法不一样。


四、磁盘的物理结构:盘片、磁道、扇区、柱面

先看磁盘的整体结构。

磁盘物理结构

4.1 磁道和扇区

磁盘盘面不是一个整体,逻辑上会被划分成一圈一圈的同心圆,每一个同心圆叫做磁道

磁道也不是一个连续整体,它还会被切成一小段一小段的扇形区域,这些小区域叫做扇区

磁道与扇区示意图一

磁道与扇区示意图二

扇区是磁盘读写数据的基本单位。本文沿用传统模型,把一个扇区按 512 字节理解。现代硬盘还可能使用 4KB 原生扇区(4Kn),或者对上层兼容成 512 字节逻辑扇区(512e),但不影响后面理解 CHS、LBA 和文件系统块的主线。

这里要注意:

重点: 磁盘不是按单个字节读写,而是按扇区这样的块状单位读写。

如果外设每次只处理一个字节,效率会非常低,所以磁盘天然属于块设备。


4.2 多个盘面和多个磁头

真实磁盘通常不止一个盘面。一个盘片有上下两个面,每个面都可能对应一个磁头。

多盘面和磁头结构

盘片在旋转,磁头臂带着磁头在半径方向上移动。多个磁头通常是共进退的,也就是说,它们会一起移动到相同半径的位置。

多个磁头共进退并形成柱面

当多个盘面上相同半径位置的磁道组合在一起时,就形成了柱面

可以这样理解:

  • **一个盘面上的某个同心圆:**磁道
  • **多个盘面上相同半径的磁道组合:**柱面

这个“柱面”是理解 CHS 寻址非常重要的概念。


五、如果要读写一个扇区,磁盘要做什么?

假设现在要把 512 字节写入某个扇区,磁盘大致要经历这几个动作:

  1. 磁头臂移动,让磁头到达目标柱面;
  2. 盘片继续旋转,等待目标扇区转到磁头下面;
  3. 根据目标盘面选择对应磁头;
  4. 磁头完成读或写。

这也解释了机械磁盘为什么慢:

它不是纯电子设备,还涉及机械移动和等待旋转。

磁头移动到目标磁道的时间叫寻道时间,等待目标扇区转到磁头下方的时间叫旋转延迟。机械动作越多,访问就越慢。


六、CHS 寻址:用柱面、磁头、扇区定位数据

在真实物理磁盘中,可以用三个参数定位一个扇区:

参数 英文 含义
C Cylinder 柱面
H Head 磁头
S Sector 扇区

这就是 CHS 寻址。

定位过程可以理解成:

  1. 先确定访问哪个柱面;
  2. 再确定柱面中的哪个磁头/盘面;
  3. 最后确定该磁道上的第几个扇区。

CHS 寻址相关结构

CHS 定位扇区示意图

磁盘容量也可以用这些参数算出来:

磁盘容量计算公式:

磁盘容量 = 磁头数 × 磁道(柱面)数 × 每道扇区数 × 每扇区字节数

常见概念整理如下:

名称 含义
扇区 sector 磁盘读写的最小物理单位,通常 512 字节
磁道 track 盘面上一圈同心圆
柱面 cylinder 多个盘面上相同半径的磁道组合
磁头 head 每个盘面通常对应一个读写磁头
盘片 platter 磁盘中的圆形存储介质

早期 CHS 寻址比较直接,但它有容量限制。传统 CHS 中常见参数位数是:磁头地址 8 bit、柱面地址 10 bit、扇区地址 6 bit,一个扇区 512 字节。按这种方式计算,最大容量大约是:

传统 CHS 模型下的理论容量:256 × 1024 × 63 × 512B ≈ 8064MB

如果按 1MB = 1000000B 来算,大约是 8.4GB。

这个容量对早期磁盘够用,但对现代磁盘显然不够,所以后来更常用的是 LBA。

关于现代磁盘的说明: CHS 更适合用来理解机械磁盘的盘片、磁头、柱面和扇区。现代操作系统主要使用 LBA,硬盘或 SSD 控制器再把逻辑地址映射到真实存储位置。这个映射不一定还是简单的“LBA 直接换算成传统 CHS”,尤其 SSD 根本没有磁头和柱面。


七、磁盘的逻辑结构:把磁盘看成一个一维数组

CHS 是从物理结构角度理解磁盘,但操作系统并不希望天天关心柱面、磁头、扇区这些物理细节。

可以换个想法:磁带卷起来是圆的,但如果把它拉直,就是一条线性结构。

磁带可以拉直成线性结构

磁盘也是类似的。虽然物理上是一圈圈磁道,但逻辑上可以把所有扇区排成一条线。

将磁盘逻辑上拉直

这样每一个扇区就有了一个线性编号,这个编号叫:

LBA,Logical Block Address,逻辑块地址。

磁盘扇区线性化

每个扇区都有一个 LBA 下标

从操作系统角度看,磁盘就像一个很大的数组:

磁盘可以按顺序抽象为:

  • sector[0]
  • sector[1]
  • sector[2]
  • ...

数组下标就是 LBA。

这样做最大的好处是:

LBA 的价值: 操作系统只需要使用一个数字,就可以描述一个扇区位置。

至于这个 LBA 最后如何转换成真实磁盘上的柱面、磁头、扇区,交给磁盘控制器和固件去做。


八、真实视角:磁道、柱面和整盘都可以被线性化

这里再从真实结构上顺一遍。

一个细节:磁头臂上的磁头是共进退的。

磁头共进退和柱面示意

柱面不是单个盘面上的一圈,而是多个盘面上相同半径的磁道组合。

柱面是多个盘面相同半径磁道的组合

某一个盘面上的某个磁道展开后,可以看成一维数组。

单个磁道展开成一维结构

整个柱面展开后,可以看成二维结构。

柱面展开成二维结构一

柱面展开成二维结构二

整个磁盘可以看成多个二维结构组合起来,类似三维数组。

整盘展开后的逻辑结构

但无论是二维还是三维,在 C/C++ 中最终都可以按连续地址线性化管理。

多维结构也可以线性化

所以从文件系统使用者角度看:

磁盘最终就是一个元素为扇区的一维数组,数组下标就是 LBA。


九、CHS 与 LBA 的转换公式

虽然操作系统一般直接使用 LBA,但理解 CHS 和 LBA 的转换有助于把磁盘结构彻底串起来。

假设:

每个柱面的扇区总数 = 磁头数 × 每磁道扇区数

9.1 CHS 转 LBA

公式:

CHS 转 LBA 公式:

LBA = C ×(磁头数 × 每磁道扇区数)+ H × 每磁道扇区数 + S - 1

其中:

  • C 是柱面号;
  • H 是磁头号;
  • S 是扇区号;
  • 扇区号通常从 1 开始;
  • LBA 从 0 开始,所以最后要 -1
9.1.1 CHS 转 LBA 计算示例

假设一块教学模型磁盘有 4 个磁头,每条磁道有 16 个扇区,现在要计算:

参数 数值
柱面号 C 2
磁头号 H 1
扇区号 S 5

代入公式:

代入计算:

LBA = 2 × (4 × 16) + 1 × 16 + 5 - 1
= 128 + 16 + 4
= 148

所以 (C=2, H=1, S=5) 对应的 LBA 是 148

9.2 LBA 转 CHS

公式:

LBA 转 CHS 公式:

  • C = LBA //(磁头数 × 每磁道扇区数)
  • H =(LBA %(磁头数 × 每磁道扇区数))// 每磁道扇区数
  • S =(LBA % 每磁道扇区数)+ 1

这里 // 表示整除。

9.2.1 用 LBA=148 反推 CHS

仍然使用 4 个磁头、每磁道 16 个扇区:

反推过程:

  • C = 148 // (4 × 16) = 2
  • H = (148 % 64) // 16 = 1
  • S = (148 % 16) + 1 = 5

最终又得到 (C=2, H=1, S=5),与前面的结果对应。

CHS 与 LBA 转换示意

到这里可以得到一个重要结论:

对现代操作系统来说,主要使用的是 LBA。设备控制器和固件负责把逻辑地址映射到实际存储位置;CHS 在本文中主要承担物理结构教学模型的作用。


十、引入文件系统:为什么要有块 block?

从这里开始,就进入软件部分了。

磁盘的物理最小读写单位是扇区,常见大小是 512 字节。但操作系统访问文件时,一般不会一个扇区一个扇区地读,这样效率太低。

所以文件系统会引入“块”这个概念。

核心概念: block 是文件系统层面对磁盘数据管理的基本单位。

常见块大小是 4KB,也就是连续 8 个 512 字节扇区组成一个块。

文件系统中的块 block

这样文件系统管理文件内容,就从管理扇区变成管理块。

如果知道某个扇区的 LBA,那么:

已知 LBA:块号 = LBA / 8

如果知道块号,也能反推块内扇区:

已知块号:LBA = 块号 × 8 + n

其中 n 表示块内第几个扇区,按本文 4KB 块模型取值范围是 0~7

LBA 与块号的关系

为什么不直接让 OS 以 512 字节扇区为单位管理?主要有两个原因:

  1. 提高效率:一次读写 4KB 比频繁读写 512B 更划算;
  2. 解耦硬件:如果磁盘底层技术变化,文件系统不至于完全被物理扇区绑死。

10.1 块设备和字符设备有什么区别?

磁盘属于块设备。块设备通常按固定大小的数据块读写,并且支持随机访问,也就是可以通过地址直接定位到某一块数据。

键盘、串口这类设备更接近字符设备,数据通常按顺序形成字符流,上层一般不能像访问磁盘那样随意跳到“第几个字符”读取。

对比项 块设备 字符设备
常见设备 磁盘、SSD、U 盘 键盘、串口、部分终端设备
数据组织 按块组织 按字节流/字符流组织
随机访问 通常支持 通常不支持任意 seek
典型特点 容量大,适合文件系统 连续输入输出

文件系统通常建立在块设备上,因为它需要根据块号快速定位文件内容和管理信息。


十一、引入分区:文件系统的载体是分区

一块磁盘通常可以分成多个分区。Windows 下常见的 C 盘、D 盘、E 盘,本质上就是不同分区。

Linux 下虽然“一切皆文件”,但是底层仍然有磁盘分区。文件系统通常建立在分区之上:

  • 磁盘
    • 分区
      • 文件系统
        • 文件和目录

也就是说:

核心结论: 文件系统的载体是分区。

一个磁盘上可以有多个分区,因此也就可以有多套文件系统。

早期磁盘分区时常用柱面作为边界,因为柱面是磁头共进退后的一个自然逻辑单位。可以把磁盘上的柱面平铺开,再划分不同分区。

这里用柱面划分分区,是为了配合前面的 CHS 模型理解。现代分区工具通常直接按照 LBA 起始位置和结束位置划分分区,不再要求分区边界严格对齐传统物理柱面。

柱面平铺与分区示意一

柱面平铺与分区示意二

柱面平铺与分区示意三

不过分区本身仍然很大,直接管理起来不方便,所以 Ext 文件系统会继续把分区划分成多个块组,也就是后面要讲的 Block Group。


十二、上篇总结

这篇主要解决的是:文件系统正式登场前,磁盘到底是什么样的。

几个核心结论要记住:

  1. 未打开的文件主要存放在磁盘上,打开后的文件才进入内核内存结构中管理。
  2. 磁盘属于块设备,机械磁盘速度慢但容量大、价格便宜。
  3. 扇区是磁盘读写的基本物理单位,传统大小一般是 512 字节。
  4. 磁道是一圈同心圆,扇区是磁道上的一小段,柱面是多个盘面上相同半径磁道的组合。
  5. CHS 通过柱面、磁头、扇区定位数据,但容量表达和使用都不够方便。
  6. LBA 把磁盘抽象成一个一维数组,操作系统只需要使用线性下标访问扇区。
  7. 文件系统不会直接以扇区为主要管理单位,而是引入 block,常见大小是 4KB。
  8. 文件系统建立在分区上,分区继续划分成块组,后面 Ext 的 inode、位图、数据块都会围绕块组展开。

下一篇就继续往下看:Ext 文件系统如何通过 Super Block、GDT、Block Bitmap、Inode Bitmap、Inode Table 和 Data Blocks 管理文件。

资源分享
【Linux】文件描述符背后的底层逻辑:重定向、VFS 与用户态缓冲区
【Linux基础IO】printf 为什么会写进文件?一文搞懂重定向和 dup2 底层原理
【Linux】基础 I/O 详解:从 fopen 到 open,终于搞懂 fd 为什么从 3 开始

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐