目录树、路径解析与简易 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')) // hello

FAT(文件分配表):用一张"链表索引表"记录每个块的下一个块号(块 5 → 块 9 → 块 2 → 结束),适合顺序读、实现简单(早期 DOS/SD 卡常用)。对比 inode 的"直接记录所有块指针",FAT 随机访问稍弱但元数据极小。

名词解释

课后练习

  1. 目录和普通文件在文件系统里本质区别是什么?
    • 答案:几乎没区别——目录也是文件,只是它的"内容"是一张"子项名→位置"的映射表;普通文件内容是用户数据。内核按标志位区分两者。
  2. FAT 的"链式块"相比 inode "块指针数组"有什么优劣?
    • 答案:FAT 元数据小、适合顺序读、实现简单;但随机跳转第 N 块要沿链走 N 步,且断链(坏块)会丢后半段;inode 直接数组索引随机访问更快,但大文件需多级索引。

总结

目录树与路径解析,是把"文件路径"从日常习惯变成可计算结构的最后一课。你会惊讶地发现,目录本质上"只是一种特殊的文件"——它的内容是一张名字到位置的表。打开 /a/b/c 不过是在树上做深度优先查找。FAT 与 inode 的对比则展示了文件系统设计的两种哲学:FAT 用一张全局链表表记录块的先后,简单省元数据,适合嵌入式/SD 卡;inode 直接持有所有块指针,随机访问快,是现代 Unix/Linux 的主流。我特别想强调:看似高深的"文件系统",核心抽象就三个——块(存数据)、索引(找块)、命名(树)。理解了它们,你看 cp/mv(其实只是改目录项或块指针)、看 Docker 镜像层、看数据库页文件,都能映射到这套模型。文件系统不是魔法,是你已经亲手实现过三遍的思想。