目录树、路径解析与简易 FAT 分配
本节目标
- 理解目录如何"只是另一种文件"
- 手写路径解析(/a/b/c 逐级查找)
- 对比 inode 与 FAT 两种分配方式
目录即文件:目录的内容是一张"名字 → inode/块号"的映射表。打开 /a/b/c 就是逐级查表。
// 运行环境:Node.js 14+
// 保存为 aj-l10.js,执行:node aj-l10.js
class DirFS {
constructor() {
this.fs = {} // 块存储(简化:直接用字符串)
this.dir = {} // 目录:名 -> 内容(文件字符串 或 子目录对象)
}
// 解析路径:按 / 逐级进入目录
_resolve(path) {
const parts = path.split('/').filter(Boolean)
let cur = this.dir
for (let i = 0; i < parts.length - 1; i++) {
cur = cur[parts[i]] // 进入子目录
if (typeof cur !== 'object') throw new Error('路径不存在: ' + parts[i])
}
return { parent: cur, name: parts[parts.length - 1] }
}
write(path, content) {
const { parent, name } = this._resolve(path)
parent[name] = content // 文件就是一段字符串
}
mkdir(path) {
const { parent, name } = this._resolve(path)
parent[name] = {}
}
read(path) {
const { parent, name } = this._resolve(path)
return parent[name]
}
}
// 调用示例
const d = new DirFS()
d.mkdir('/a'); d.mkdir('/a/b')
d.write('/a/b/c.txt', 'hello')
console.log(d.read('/a/b/c.txt')) // helloFAT(文件分配表):用一张"链表索引表"记录每个块的下一个块号(块 5 → 块 9 → 块 2 → 结束),适合顺序读、实现简单(早期 DOS/SD 卡常用)。对比 inode 的"直接记录所有块指针",FAT 随机访问稍弱但元数据极小。
名词解释
- 目录树(Directory Tree):以树形组织的命名空间,根是
/,每个目录节点指向子文件/子目录。路径解析就是树的深度优先查找。 - 路径解析(Path Resolution):把
/a/b/c拆成节点序列逐层查找,遇到不存在的目录即报错。绝对路径从根开始,相对路径从当前目录开始。 - FAT(文件分配表):一种用"块号链表"管理文件占用的方案;每个块在表里记下"下一个块号",链式串联文件所有块。
课后练习
- 目录和普通文件在文件系统里本质区别是什么?
- 答案:几乎没区别——目录也是文件,只是它的"内容"是一张"子项名→位置"的映射表;普通文件内容是用户数据。内核按标志位区分两者。
- FAT 的"链式块"相比 inode "块指针数组"有什么优劣?
- 答案:FAT 元数据小、适合顺序读、实现简单;但随机跳转第 N 块要沿链走 N 步,且断链(坏块)会丢后半段;inode 直接数组索引随机访问更快,但大文件需多级索引。
总结
目录树与路径解析,是把"文件路径"从日常习惯变成可计算结构的最后一课。你会惊讶地发现,目录本质上"只是一种特殊的文件"——它的内容是一张名字到位置的表。打开 /a/b/c 不过是在树上做深度优先查找。FAT 与 inode 的对比则展示了文件系统设计的两种哲学:FAT 用一张全局链表表记录块的先后,简单省元数据,适合嵌入式/SD 卡;inode 直接持有所有块指针,随机访问快,是现代 Unix/Linux 的主流。我特别想强调:看似高深的"文件系统",核心抽象就三个——块(存数据)、索引(找块)、命名(树)。理解了它们,你看 cp/mv(其实只是改目录项或块指针)、看 Docker 镜像层、看数据库页文件,都能映射到这套模型。文件系统不是魔法,是你已经亲手实现过三遍的思想。