Files
obsidian/操作系统/05_磁盘空间管理/05_磁盘空间管理.md

18 KiB
Raw Blame History

第05讲磁盘空间管理

🎯 本节目标理解磁盘空间的外存组织方式掌握FAT/NTFS/Ext2文件系统的结构理解空闲空间管理和磁盘容错机制

📋 前置知识


🤔 为什么需要这个?

当你保存一个4GB的电影文件时操作系统需要决定把文件存在磁盘的哪些位置如何记录哪些空间已用、哪些空闲如果某个磁盘块坏了怎么办不同的组织方式会直接影响文件的读写速度和磁盘空间利用率。

生活比喻

  • 连续分配就像电影院的连续座位:一整排坐在一起,找人很快,但如果有零散空位就浪费了
  • 链接分配就像寻宝游戏每个地点告诉你下一个地点在哪灵活但不能直接跳到第N个
  • 索引分配就像书的目录:通过目录直接找到对应页码,高效且灵活

📖 核心概念

1. 外存组织方式

文件在磁盘上的存放方式有三种基本策略:

graph TD
    A[外存组织方式] --> B[连续组织
顺序文件]
    A --> C[链接组织
隐式链接/FAT]
    A --> D[索引组织
单级/多级/混合]

    B --> B1["优点:顺序和随机访问都快"]
    B --> B2["缺点:外部碎片,不能动态增长"]
    C --> C1["优点:消除了外部碎片"]
    C --> C2["缺点:不能高效随机访问"]
    D --> D1["优点:支持随机访问,无外部碎片"]
    D --> D2["缺点:索引块有开销"]

    style A fill:#e1f5fe

连续组织(顺序文件)

graph LR
    A[文件目录] -->|"起始块号=2, 长度=3"| B[磁盘块2]
    B --> C[磁盘块3]
    C --> D[磁盘块4]

    style B fill:#e8f5e9
    style C fill:#e8f5e9
    style D fill:#e8f5e9
  • 文件占用一组连续的磁盘块
  • 目录项只需记录:起始块号 + 长度
  • 支持顺序访问和随机访问(直接计算偏移)
  • 问题:外部碎片严重,文件不能动态增长

链接组织

隐式链接:每个磁盘块末尾存储指向下一个块的指针。

graph LR
    A[目录] -->|"起始块号=2"| B[块2] -->|"指针→5"| C[块5] -->|"指针→8"| D[块8] -->|"指针→EOF"| E[结束]

    style B fill:#e1f5fe
    style C fill:#e1f5fe
    style D fill:#e1f5fe
  • 优点:消除了外部碎片,文件可以动态增长
  • 缺点:只能顺序访问,指针占用存储空间,可靠性差(一个指针损坏后续全部丢失)

显式链接FAT:将所有块的链接指针集中存放在一张**文件分配表FAT**中。

graph TD
    subgraph 文件分配表FAT
        F0["0: —"]
        F1["1: —"]
        F2["2: 5"]
        F3["3: —"]
        F4["4: —"]
        F5["5: 8"]
        F6["6: —"]
        F7["7: —"]
        F8["8: EOF"]
    end

    subgraph 磁盘块
        D2[块2]
        D5[块5]
        D8[块8]
    end

    D2 -.->|"FAT[2]=5"| D5
    D5 -.->|"FAT[5]=8"| D8
    D8 -.->|"FAT[8]=EOF"| STOP[结束]

    style F2 fill:#ffcdd2
    style F5 fill:#ffcdd2
    style F8 fill:#ffcdd2
  • FAT常驻内存随机访问时只需查表不需要读磁盘
  • 比隐式链接更可靠,但也更占内存

索引组织

为每个文件建立一个索引块,集中存储所有数据块的块号:

graph TD
    A[文件目录] -->|"索引块号=10"| B[索引块]
    B -->|"指针0"| C[块2]
    B -->|"指针1"| D[块5]
    B -->|"指针2"| E[块8]
    B -->|"指针3"| F[块12]

    style B fill:#fff3e0
    style C fill:#e1f5fe
    style D fill:#e1f5fe
    style E fill:#e1f5fe
    style F fill:#e1f5fe
  • 支持随机访问访问第N个数据块直接查索引块的第N个指针
  • 无外部碎片
  • 缺点:索引块本身占用空间,大文件需要多级索引

三种方式对比

特性 连续组织 链接组织 索引组织
顺序访问
随机访问 快(直接计算) 慢(需遍历链) 快(查索引表)
外部碎片 严重
文件增长 困难 容易 容易
可靠性 低(指针损坏)

2. FAT文件系统

FATFile Allocation Table是Windows早期广泛使用的文件系统。

graph TD
    subgraph FAT磁盘布局
        A[引导扇区] --> B[FAT1]
        B --> C[FAT2 备份]
        C --> D[根目录区]
        D --> E[数据区]
    end

    style A fill:#ffcdd2
    style B fill:#fff3e0
    style C fill:#fff3e0
    style D fill:#e1f5fe
    style E fill:#e8f5e9

ClusterFAT文件系统的最小分配单位由若干连续扇区组成。

FAT版本 最大分区 簇大小 FAT表项位数
FAT12 2MB 512B~4KB 12位
FAT16 2GB 2KB~32KB 16位
FAT32 2TB 4KB~32KB 32位实际28位

FAT表结构每个簇在FAT表中占一个表项表项内容的含义

表项值 含义
0 空闲簇
2~N 下一个簇的簇号
FF8H~FFFH (FAT12) 文件结束标记
FFF8H~FFFFH (FAT16) 文件结束标记
0FFFFFF8H~0FFFFFFFH (FAT32) 文件结束标记
其他特殊值 坏簇标记

FAT16的问题每个分区最多65536个簇簇最小2KB时最大分区为2GB。小文件也会浪费一个簇的空间簇内碎片

3. NTFS文件系统

NTFSNew Technology File System是Windows NT系列的现代文件系统。

graph TD
    subgraph NTFS磁盘布局
        A[引导扇区
MBR/分区表] --> B[主控文件表MFT]
        B --> C[MFT副本]
        C --> D[系统文件区]
        D --> E[用户数据区]
    end

    style A fill:#ffcdd2
    style B fill:#fff3e0
    style C fill:#fff3e0
    style D fill:#e1f5fe
    style E fill:#e8f5e9

关键特性

特性 说明
64位磁盘地址 支持超大分区理论2^64字节
MFT主控文件表 核心数据结构,每个文件/目录对应一条MFT记录
LCN逻辑簇号 从分区开头算起的绝对簇号
VCN虚拟簇号 文件内部的相对簇号从0开始
日志文件 记录文件操作日志,崩溃后可恢复一致性
文件加密 支持EFS加密文件系统
文件压缩 支持透明压缩
硬链接 多个文件名指向同一个MFT记录

MFT结构每条MFT记录通常1KB包含多个属性文件名、时间戳、数据内容等。小文件的数据直接存储在MFT记录中驻留属性无需额外数据块。

4. Ext2文件系统

Ext2是Linux的经典文件系统采用块组组织磁盘空间。

磁盘总体布局

graph LR
    subgraph "Ext2磁盘布局"
        A["引导块
Block 0"] --> B["块组0"]
        B --> C["块组1"]
        C --> D["块组2"]
        D --> E["..."]
    end

    style A fill:#ffcdd2

块组内部结构

graph TD
    subgraph "单个块组的结构"
        A["超级块 Super Block
文件系统全局信息"] --> B["块组描述符表
所有块组的描述信息"]
        B --> C["数据块位图
记录哪些块已用"]
        C --> D["inode位图
记录哪些inode已用"]
        D --> E["inode表
所有inode的数组"]
        E --> F["数据块
存放文件内容"]
    end

    style A fill:#ffcdd2
    style B fill:#ffcdd2
    style C fill:#fff3e0
    style D fill:#fff3e0
    style E fill:#e1f5fe
    style F fill:#e8f5e9

超级块Super Block存储整个文件系统的元数据

  • 块大小、inode总数、块总数、空闲块数、空闲inode数
  • 挂载计数、上次检查时间等

块组描述符Group Descriptor描述每个块组的状态

  • 本块组的块位图位置、inode位图位置、inode表位置、空闲块数等

块位图Block Bitmap每个bit对应一个数据块1=已用0=空闲

inode位图inode Bitmap每个bit对应一个inode1=已用0=空闲

inode结构128字节

graph TD
    subgraph "inode 128字节"
        A["模式/权限 2B"] --> B["所有者ID 2B"]
        B --> C["文件大小 4B"]
        C --> D["时间戳 12B
访问/修改/创建"]
        D --> E["链接计数 2B"]
        E --> F["数据块指针 60B"]
    end

    subgraph "15个地址项60字节"
        G["直接块指针 0~9
10个 × 4B"]
        H["一次间接指针
1个 × 4B"]
        I["二次间接指针
1个 × 4B"]
        J["三次间接指针
1个 × 4B"]
    end

    F --> G
    F --> H
    F --> I
    F --> J

    style A fill:#e1f5fe
    style G fill:#e8f5e9
    style H fill:#fff3e0
    style I fill:#ffcdd2
    style J fill:#fce4ec

混合索引容量计算(重点)

设盘块大小为4KB,盘块号占4字节inode有13个地址项10直接 + 1一次间接 + 1二次间接 + 1三次间接

每个盘块可存放盘块号个数:

4096 \div 4 = 1024 \text{ 个指针}
索引级别 计算过程 容量
直接块10个 10 x 4KB 40KB
一次间接 1024 x 4KB 4MB
二次间接 1024 x 1024 x 4KB 4GB
三次间接 1024 x 1024 x 1024 x 4KB 4TB
总计 约4TB
graph TD
    subgraph "混合索引寻址过程"
        A["inode"] -->|"直接块指针0~9"| B["10个数据块
共40KB"]
        A -->|"一次间接指针"| C["索引块1
1024个指针"]
        C -->|"指针0~1023"| D["1024个数据块
共4MB"]
        A -->|"二次间接指针"| E["二级索引块
1024个指针"]
        E -->|"指针0~1023"| F["1024个一级索引块"]
        F -->|"每个指向1024个数据块"| G["1024x1024个数据块
共4GB"]
        A -->|"三次间接指针"| H["三级索引块"]
        H --> I["1024个二级索引块"]
        I --> J["1024x1024个一级索引块"]
        J --> K["1024^3个数据块
共4TB"]
    end

    style A fill:#ffcdd2
    style B fill:#e8f5e9
    style D fill:#e8f5e9
    style G fill:#e8f5e9
    style K fill:#e8f5e9

寻址示例假设要读取文件的第15000个字节盘块大小4KB

  • 逻辑块号 = 15000 / 4096 = 3第4个块索引为3
  • 块内偏移 = 15000 % 4096 = 2660
  • 因为索引3 < 10所以使用直接块指针[3]找到数据块
  • 在该数据块的偏移2660处读取数据

目录项 ext2_dir_entry_2

目录在Ext2中也是文件内容是一系列目录项

字段 大小 说明
inode号 4字节 指向的inode编号
rec_len 2字节 本目录项总长度
name_len 1字节 文件名长度
file_type 1字节 文件类型(普通文件/目录/符号链接等)
name 变长 文件名不超过255字节

5. HDFS

HDFSHadoop Distributed File System是大数据领域的分布式文件系统。

graph TD
    subgraph HDFS架构
        NN["名称节点 NameNode
元数据管理
文件→块映射
块→数据节点映射"]
        DN1["数据节点 DataNode1
块1, 块3"]
        DN2["数据节点 DataNode2
块2, 块4"]
        DN3["数据节点 DataNode3
块1副本, 块5"]
    end

    NN -->|"心跳/块报告"| DN1
    NN -->|"心跳/块报告"| DN2
    NN -->|"心跳/块报告"| DN3
    DN1 -->|"数据流"| CLIENT["客户端"]

    style NN fill:#ffcdd2
    style DN1 fill:#e1f5fe
    style DN2 fill:#e1f5fe
    style DN3 fill:#e1f5fe

特点

  • 文件被分割为固定大小的块默认128MB分布在多个数据节点上
  • 每个块默认3副本分布在不同机架
  • 名称节点管理元数据(内存中),数据节点存储实际数据
  • 适合大文件顺序读取,不适合小文件和随机写入

6. 空闲空间管理

空闲表法

用一张表记录所有空闲区的起始块号和长度:

起始块号 空闲块数
2 3
10 5
20 2

适用于连续分配方式,适合少量空闲区的情况。

空闲链表法

将所有空闲磁盘块用指针链接成一个链表。分配时从链头取,回收时插入链尾。缺点是指针占用空间,分配效率低。

位示图法

用一个**位图bitmap**记录每个磁盘块的使用状态1=已用0=空闲(或反之)。

位示图示例每行16位
第0行: 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0   → 块0~3已用
第1行: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0   → 块16~31空闲
第2行: 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0   → 块32~33已用

地址转换公式

设每行有 j通常为字长如16位、32位则第 n 个磁盘块对应位示图中:

\text{行号 } i = n \div j \text{列号 } k = n \mod j

即:

n = j \times i + k

经典公式每行16位时n = 16 \times i + j

位示图法示例设磁盘共200个块每行16位位示图需要 200/16 = 13 行。

要找到第75个空闲块$i = 75 \div 16 = 4$$j = 75 \mod 16 = 11$即第4行第11列。

成组链接法UNIX

UNIX采用成组链接法管理空闲磁盘块,是空闲表法和空闲链表法的结合:

graph TD
    SB["超级块
栈:存放当前组的空闲块号
栈顶指针"] -->|"指向"| G1["空闲块组1
栈底块存储下一组的信息"]
    G1 -->|"下一组指针"| G2["空闲块组2"]
    G2 -->|"下一组指针"| G3["空闲块组3"]
    G3 -->|"下一组指针"| G4["更多组..."]

    style SB fill:#ffcdd2
    style G1 fill:#e8f5e9
    style G2 fill:#e8f5e9
    style G3 fill:#e8f5e9

分配过程

  1. 从超级块的栈中弹出一个空闲块号
  2. 如果栈中只剩一个元素(它是下一组的指针),先将该组信息读入超级块,再弹出
  3. 更新超级块

回收过程

  1. 将回收的块号压入超级块的栈中
  2. 如果栈已满,将超级块中的栈信息写入回收块(成为新组),清空栈,将回收块号作为唯一元素

优点:分配和回收只需读写超级块(内存中),效率极高。

7. 磁盘IO优化

优化技术 原理
磁盘高速缓存 在内存中开辟缓冲区缓存磁盘块,减少磁盘访问
提前读 顺序读取时,预先把后续块读入缓存
延迟写 先写入缓存,延迟到合适时机再写入磁盘
虚拟盘 用内存模拟磁盘RAM Disk速度极快但断电丢失

8. RAID

RAIDRedundant Array of Independent Disks通过多块磁盘组合提高性能和可靠性。

graph TD
    subgraph RAID0 条带化
        A0["数据块1"] --> S0[磁盘0]
        A1["数据块2"] --> S1[磁盘1]
        A2["数据块3"] --> S0
        A3["数据块4"] --> S1
    end

    subgraph RAID1 镜像
        B0["数据"] --> M0[磁盘0]
        B0 --> M1[磁盘1 镜像]
    end

    subgraph RAID5 分布式校验
        C0["数据A"] --> R0[磁盘0]
        C1["数据B"] --> R1[磁盘1]
        C2["P校验"] --> R2[磁盘2]
        C3["数据C"] --> R3[磁盘3]
    end

    style S0 fill:#e1f5fe
    style S1 fill:#e1f5fe
    style M0 fill:#e8f5e9
    style M1 fill:#fff3e0
    style R0 fill:#e1f5fe
    style R1 fill:#e1f5fe
    style R2 fill:#ffcdd2
    style R3 fill:#e1f5fe
RAID级别 原理 冗余 最少磁盘 利用率 特点
RAID0 数据条带化分布 2 100% 高性能,无容错
RAID1 数据完全镜像 100% 2 50% 高可靠,成本高
RAID3 位交叉+专用校验盘 1块校验盘 3 (N-1)/N 校验盘成瓶颈
RAID5 块交叉+分布式校验 分布式校验 3 (N-1)/N 性能与可靠性平衡

9. 磁盘容错

容错级别 技术 内容
SFT-I 一级容错 双份目录和FAT表、热修复重定向写入坏块时重定向到备用块
SFT-II 二级容错 磁盘镜像(同一控制器两个磁盘)、磁盘双工(不同控制器两个磁盘)
集群容错 三级容错 多台服务器组成集群,一台故障其他接管

💻 动手实践

查看文件系统信息

# 查看磁盘使用情况
df -h

# 查看inode使用情况
df -i

# 查看文件系统类型和块大小
tune2fs -l /dev/sda1 | grep -E "Block size|Inode count"

# 查看文件的块分配
stat filename

# 查看文件的inode号
ls -i filename

🔗 知识关联


📝 思考题

  1. FAT表计算一个FAT16分区每簇4KB最多能管理多大的分区为什么
  2. Ext2容量计算如果盘块大小为1KB而非4KB盘块号占4字节那么混合索引支持的最大文件是多少提示每个间接块只能放256个指针
  3. inode寻址给定盘块大小4KB盘块号4字节要读取文件偏移5GB处的数据需要经过几级间接索引
  4. 成组链接法如果超级块栈最多容纳100个空闲块号那么分配第101个空闲块时会发生什么
  5. RAID选择一个视频监控系统需要大容量存储、持续写入、偶尔丢失可接受应选择RAID几为什么

📚 扩展阅读