文件系统即数据结构
文件系统本质上是磁盘上的数据结构,包含树形目录结构和两个分配池(inode 和块)。
MIT 6.1810 · 第 17 课 · rtm
文件系统是操作系统中最重要的抽象之一。本节课探讨文件系统的设计、实现与性能优化,以 xv6 的文件系统为案例进行深入分析。
文件系统提供了以下核心功能:
fd = open("x/y", -);
write(fd, "abc", 3);
link("x/y", "x/z"); -- 现在文件有两个名字!
unlink("x/y");
write(fd, "def", 3);
close(fd);
// 文件 y/z 包含 abcdef
| 选择 | Unix 决策 | 其他可能 |
|---|---|---|
| 对象 | 文件 | 原始磁盘空间、数据库记录 |
| 内容 | 字节数组 | 行/列、BTree |
| 大小 | 可随时追加 | 预分配 |
| 命名 | 人类可读 | 对象 ID |
| 组织 | 目录层次结构 | 扁平命名空间 |
| 同步 | 无(无锁、无版本) | 事务、锁 |
fd 指向的是什么?
硬链接的含义:
系统调用层
↓
名称 | 文件描述符 (FDs)
↓
inode 层
↓
inode 缓存
↓
日志 (log)
↓
块缓存 (buffer cache)
↓
virtio_disk 驱动
| 类型 | 特点 | 随机访问 | 顺序访问 |
|---|---|---|---|
| 机械硬盘 (HDD) | 大容量、低成本、慢 | 5-10ms(寻道+旋转) | ~100 MB/s |
| 固态硬盘 (SSD) | 更快、更贵、复杂 | ~100 μs | ~500 MB/s |
块号 0 1 2 33 46 47+
内容 未用 超级块 日志 inode 位图 数据块
(31块) (数组) (使用表) (文件/目录内容)
// kernel/fs.h
struct dinode {
short type; // 文件类型(空闲、文件、目录、设备)
short nlink; // 链接数
uint size; // 文件大小
uint addrs[12+1]; // 直接块 + 1 个间接块指针
};
逻辑块号 = 8000 / BSIZE (= 1024) = 7
inode.addrs[7] 存储了磁盘块号
// 目录项结构 (dirent)
struct dirent {
ushort inum; // inode 号
char name[14]; // 文件名(最多 14 字节)
};
// 空闲目录项的 inum == 0
$ echo > x
bwrite 34 ialloc // 分配 inode
bwrite 34 iupdate // 更新 inode(设置 nlink)
bwrite 47 writei // 写入目录项
bwrite 33 iupdate // 更新目录 inode
bwrite 34 iupdate // itrunc 新 inode
$ echo a > x
bwrite 46 balloc // 在位图中分配一个块
bwrite 914 bzero // 清零分配的块
bwrite 914 writei // 写入数据
bwrite 34 iupdate // 更新 inode(长度和新块)
$ rm x
bwrite 47 writei // 删除目录项
bwrite 33 iupdate // 更新目录 inode
bwrite 34 iupdate // 减少链接数
bwrite 46 bfree // 释放数据块
bwrite 34 iupdate // itrunc:清零长度
bwrite 34 iupdate // 标记 inode 为空闲
prev ... head ... next
bget 重用 bcache.head.prev("尾部")
brelse 将块移到 bcache.head.next
这是一种近似的 LRU 策略。
路径名查找可能涉及多个磁盘块:
并发挑战:unlink 可能与 lookup 同时发生
解决方案 (namex):
文件系统本质上是磁盘上的数据结构,包含树形目录结构和两个分配池(inode 和块)。
Unix API 的每个选择(字节数组、目录层次、无同步)都深刻影响了文件系统的实现。
磁盘很慢,块缓存和 inode 缓存是性能的关键。
文件系统操作通常涉及多个磁盘写入,崩溃可能导致不一致——这是下节课的主题。
Q1:xv6 文件系统的最大文件大小是多少?
Q2:为什么文件系统需要 inode?
Q3:块缓存的替换策略是什么?