一个 filesystem(文件系统)把一个扁平的磁盘 block 数组变成文件和目录的层级结构,管理元数据和空闲空间以使其既可靠又快速。
Inode:一个文件 = 元数据 + block 指针
在 UNIX 文件系统中,每个文件都是一个 (index node)——一个持有文件(大小、所有者、权限、时间戳、link count)和的结构。注意 inode 存储什么:。名字存在于**目录(directory)**中,而目录只是把 名字 → inode 号 映射的文件:
directory "docs" inode 1042 data blocks
┌───────────────────┐ ┌──────────────┐ ┌────────┐
│ "report.txt" →1042│───────────▶│ mode, uid │──ptr───▶│ block │
│ "notes.txt" →1050│ │ size, times │──ptr───▶│ block │
└───────────────────┘ │ link count=2 │ └────────┘
│ block ptrs │──(indirect)──▶ ...
└──────────────┘
这就是为什么一个 hard link 只是指向同一个 inode 的第二个目录 entry(link count 为 2),也是为什么 rm 实际上是 unlink——它移除一个名字,只有当 link count 归零时才释放 inode。
单个逻辑操作(例如一次 append)会触及多个 block——inode、空闲空间 bitmap、数据。中途崩溃会让文件系统处于不一致状态。一个 journal 先把打算做的更改写入一个 log,然后才应用它们;崩溃之后文件系统重放(replay)或丢弃(discard)journal,而不是做一次缓慢的全盘扫描(fsck)。各种模式在安全与速度之间权衡:只 journal 元数据(快,常见)与元数据加数据都 journal。
Kernel 在 page cache 中把文件数据缓存在 RAM 里,因此重新读取命中内存,写入被**缓冲(buffered)**并稍后 flush(write-back):
buffered I/O (default): app ↔ page cache ↔ disk → cached, async writeback, fast re-reads
direct I/O (O_DIRECT): app ↔ disk → bypass the cache; DBs manage their own
fsync(): force buffered data to durable storage (required for real durability)
Buffered I/O 很快,但“已写入”的数据在 fsync 之前并不持久(durable);数据库常常使用 direct I/O 加上自己的 buffer pool,以精确控制 cache 与持久性。
文件系统内部机制解释了日常行为:为什么 hard link 能工作、为什么删除一个仍被某进程打开的文件在它关闭前不会释放空间、为什么 fsync 对持久性至关重要(省略它是一个经典的数据丢失 bug)、以及为什么第二次读取一个文件是瞬时的(page cache)。senior 工程师在为数据库选择 buffered vs direct I/O、为 cache 调整 RAM 大小、或调试崩溃一致性问题时,会对这些进行推理。
一个包含详细解答的 IT 面试题库——从初级到高级。
捐赠