很多人在学操作系统的时候,文件系统这一章最容易卡住,尤其是讲到磁盘空间分配时,连续分配、链式分配、索引分配这几个概念来回对比,总感觉懂了又不太会用。今天这篇Day 46,我把文件系统里最基础的两种分配方式——连续分配与链式分配,掰开揉碎讲清楚,从实现原理到优劣对比,再到怎么用代码模拟一遍,一次把这块补扎实。
这篇文章是操作系统学习系列里的文件系统核心篇,适合正在复习操作系统期末、准备考研408、或者面试前临时抱佛脚的朋友,也适合那些想彻底搞懂文件系统底层原理但不想只背结论的人。看明白这篇,后面再看ext4的extent机制、NTFS的run列表、FAT文件系统,都会有"原来如此"的感觉。
1. 先理清楚:文件系统到底在分配什么东西
1.1 磁盘空间管理的本质问题
文件系统说白了,就是帮用户把"逻辑上连续的一整块数据"映射到"物理上分散的多个存储单位"上。一个文件可能只有几KB,但磁盘上不可能每次都为这个文件划一块刚好一样大的专用区域,因为文件会增删改,磁盘空间会被不断占用和释放。所以文件系统必须管理一个核心问题:文件的数据块,到底怎么放、放哪里、怎么找到它们。
这个"怎么放"的策略,就是磁盘空间分配方式。它直接决定了文件读取快不快、空间利用率高不高、文件能不能动态扩展、系统崩溃后能不能恢复。连续分配和链式分配是两种最原始、思路最简单的方案,也是理解现代文件系统的地基。
1.2 块、扇区、簇:分配的最小单位
讲分配方式之前,必须先确定"分配单位"。磁盘是按扇区读写的,一个扇区传统上是512字节,但文件系统不会一个扇区一个扇区地管,那样元数据太庞大了。文件系统会把连续的若干个扇区合成一个"块"(block),在Linux的ext系列里默认是4KB,也就是把8个512字节的扇区拼成一个块。
块也可以叫簇,FAT文件系统里就习惯叫簇(cluster)。文件存储时按块分配,也就是说一个文件哪怕只有1个字节,也得占一整块4KB。这是文件系统空间利用率的第一个损耗来源,叫"内部碎片"——块内部没用完的那部分。这个理解了,后面再看"外部碎片"才不混乱。内碎片是块内部的浪费,外碎片是空闲空间被切得太碎、无法满足连续分配需求的浪费。
1.3 分配策略要回答的三个问题
任何分配方式都必须回答三个问题:
- 文件元数据里记录什么信息,才能在需要时找到文件所有数据块?
- 空闲空间怎么管理,新文件来了能不能快速找到合适的位置?
- 文件要增长时,现有的方案支不支持原地扩展?如果不支持,代价是什么?
连续分配和链式分配,就是两组截然不同的答案。连续分配说"我一口气给你一段连续空间,记下起点和长度就行";链式分配说"我不保证连续,但每个块里都记着下一块在哪,你顺着链子走就行"。理解了这两句话,后面所有细节都是展开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 连续分配:最直觉的方案,也是最脆的方案
2.1 核心思想与目录项结构
连续分配太好理解了,就好比图书馆里把一本书的所有页都放在书架相邻的位置。文件系统向磁盘申请空间时,一次性拿到一组连续的块,然后在文件的目录项里记录两个关键数字:起始块号和文件总块数(或者文件字节长度)。读到文件时,从起始块开始,按块号递增读下去就行,不需要任何额外的查找。
目录项里存的信息大概是这样的:
| 字段 | 含义 |
|---|---|
| 文件名 | 用户看到的名称 |
| 起始块号(start_block) | 文件第一块数据所在的物理块号 |
| 文件长度(file_size) | 文件占用的字节数或块数 |
这个结构极其简洁,查询文件时一次定位,不需要遍历什么东西。如果我们要读文件里的第N个字节,可以直接算:物理块号 = 起始块号 + (N / 块大小)。这种"算术寻址"的能力,是连续分配最大的性能优势。
2.2 为什么连续分配读起来飞快
如果你了解一点机械硬盘的物理结构,就知道磁头移动(寻道)是访问磁盘最贵的操作之一。读一个块可能只需要几十微秒,但把磁头从当前磁道移到目标磁道可能要几毫秒,两者差了上百倍。
连续分配正好把文件所有数据块挨在一起,
