块设备、inode 与超级块设计
本节目标
- 理解文件系统三大核心概念:块 / inode / 超级块
- 用手写 Buffer 模拟一个极简文件系统
- 体会"文件内容如何被索引到"
三大件:
- 块(block):磁盘读写的最小单位(如 4KB 一组),文件数据存在块里。
- inode:文件的"身份证 + 索引表",记录文件大小、权限、以及指向哪些块的指针。
- 超级块(superblock):整个文件系统的"总台账",记录块总数、inode 总数、空闲位图位置等。
// 运行环境:Node.js 14+
// 保存为 aj-l9.js,执行:node aj-l9.js
const BLOCK = 16 // 演示用小块大小
class MiniFS {
constructor(totalBlocks) {
this.super = { magic: 0x1234, totalBlocks, freeBitmap: new Array(totalBlocks).fill(1) } // 1=空闲
this.blocks = Array.from({ length: totalBlocks }, () => Buffer.alloc(BLOCK))
this.inodes = {} // id -> {size, blocks:[...]}
this.nextInode = 1
}
allocBlock() {
const i = this.super.freeBitmap.indexOf(1)
if (i === -1) throw new Error('磁盘满')
this.super.freeBitmap[i] = 0
return i
}
writeFile(name, content) {
const data = Buffer.from(content)
const blocks = []
for (let off = 0; off < data.length; off += BLOCK) {
const b = this.allocBlock()
data.copy(this.blocks[b], 0, off, off + BLOCK)
blocks.push(b)
}
this.inodes[name] = { size: data.length, blocks }
return this.nextInode++
}
readFile(name) {
const inode = this.inodes[name]
const out = Buffer.alloc(inode.size)
let w = 0
for (const b of inode.blocks) { this.blocks[b].copy(out, w); w += BLOCK }
return out.slice(0, inode.size).toString()
}
}
// 调用示例
const fs = new MiniFS(8)
fs.writeFile('hello.txt', '你好,文件系统!')
console.log(fs.readFile('hello.txt')) // 你好,文件系统!名词解释
- 块设备(Block Device):以"固定大小块"为单位随机读写的存储(硬盘/SSD)。文件系统在其上管理块的使用。
- inode(索引节点):Unix 文件系统里描述文件元数据(大小、时间戳、块指针)的结构。一个文件对应一个 inode,文件名只是指向它的链接。
- 超级块(Superblock):文件系统全局元数据的根,记录总块数、空闲表位置等;损坏会导致整个分区无法挂载。
课后练习
- 为什么文件系统用"块"而不用"字节"管理空间?
- 答案:按字节管理元数据开销爆炸;以块为单位(如 4KB)既能减少寻址次数,又让位图/索引规模可控,是空间与效率的折中。
- inode 损坏会怎样?
- 答案:虽然文件名还在目录里,但"文件对应哪些块、多大"的信息丢失,文件系统无法拼出内容,文件相当于"找不回来"(需靠备份或 fsck 修复)。
总结
手写迷你文件系统,是把"磁盘"从黑盒变成可触摸对象的一课。你会发现,所谓"保存一个文件",底层不过是在一张位图里找几个空闲块、把字节写进去、再在 inode 里记下"这些块属于这个文件"。超级块则是整个系统的"户口本"——它一坏,所有户口都查不到。我特别想点出 inode 这个设计的精妙:它把"文件内容"和"文件名"解耦,于是同一份数据可以有多个名字(硬链接),删除文件名也不等于删除数据。理解块/inode/超级块,你看 ls -i(看 inode 号)、df(看块用量)、fsck(修超级块)就不再是天书,而是你亲手实现过的概念。这种"从字节到文件"的认知,是后端、存储、甚至数据库索引设计的共同地基。