实验:文件系统
在本实验中,您将向 xv6 文件系统添加大文件和符号链接。
在编写代码之前,应该阅读xv6书中的“第8章:文件系统”并研究相应的代码。
获取实验室的 xv6 源代码并查看分支util:
$ git fetch
$ git checkout fs
$ make clean
大文件(中等)
在此作业中,您将增加 xv6 文件的最大大小。目前 xv6 文件限制为 268 个块或 268*BSIZE 字节(xv6 中 BSIZE 为 1024)。这个限制来自于一个xv6 inode包含12个“直接”块号和一个“单间接”块号,这是指一个块最多可以容纳256个块号,总共12+256=268块。
该bigfile命令创建它可以创建的最长文件,并报告该大小:
$ bigfile
..
wrote 268 blocks
bigfile: file is too small
$
测试失败,因为bigfile期望能够创建具有 65803 个块的文件,但未修改的 xv6 将文件限制为 268 个块。
您将更改 xv6 文件系统代码以支持每个 inode 中的“双间接”块,其中包含 256 个单间接块地址,每个块最多可包含 256 个数据块地址。结果将是一个文件最多可以包含 65803 个块,或 256*256+256+11 个块(11 而不是 12,因为我们将牺牲双间接块的直接块号之一) 。
准备工作
该mkfs程序创建xv6文件系统磁盘映像并确定文件系统总共有多少块;该大小由FSSIZEin控制kernel/param.h。您将看到FSSIZE本实验的存储库中的块数设置为 200,000 个。您应该mkfs/mkfs在 make 输出中看到以下输出:
nmeta 70 (boot, super, log blocks 30 inode blocks 13, bitmap blocks 25) blocks 199930 total 200000
这一行描述了构建的文件系统mkfs/mkfs:它有 70 个元数据块(用于描述文件系统的块)和 199,930 个数据块,总共 200,000 个块。如果在实验过程中的任何时候您发现自己必须从头开始重建文件系统,您可以运行make clean来重建 fs.img。
看什么
磁盘 inode 的格式由struct dinode定义于fs.h中。如果您对NDIRECT、NINDIRECT、MAXFILE和addrs[]的元素特别感兴趣,请参阅 xv6 文本中的图 8.3,了解标准 xv6 inode 的图表。
在磁盘上查找文件数据的代码bmap()位于fs.c.看一下它并确保您了解它在做什么。bmap()在读取和写入文件时都会被调用。写入时,bmap()根据需要分配新块来保存文件内容,并根据需要分配间接块来保存块地址。
bmap()处理两种块号。参数bn是一个“逻辑块号”——文件内相对于文件开头的块号。中的块编号ip->addrs[]以及 的参数bread()是磁盘块编号。您可以将其bmap()视为将文件的逻辑块号映射到磁盘块号。
你的工作
修改
bmap(),使其除了直接块和单间接块之外还实现双重间接块。您只需拥有 11 个直接块(而不是 12 个)即可为新的双重间接块腾出空间;您不能更改磁盘上 inode 的大小。的前11个元素ip->addrs[]应该是直接块jjk;第 12 个应该是一个单间接块(就像当前的块一样);第 13 个应该是新的双重间接块。当bigfile写入 65803 个块并usertests -q成功运行时,您就完成了此练习:
$ bigfile
..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
wrote 65803 blocks
done; ok
$ usertests -q
...
ALL TESTS PASSED
$
bigfile运行至少需要一分半钟。
提示:
- 确保你明白
bmap()。写出ip->addrs[]、它所指向的间接块、双间接块和单间接块以及数据块之间的关系图。确保您理解为什么添加双重间接块会使最大文件大小增加256*256块(实际上是 -1,因为您必须将直接块的数量减少 1)。 - 考虑如何使用逻辑块号对双重间接块及其指向的间接块进行索引。
- 如果更改 的定义,则可能必须更改
INNDIRECT的声明。确保和的数组中元素数量相同。 - 如果更改 的定义
NDIRECT,请确保创建一个新的fs.img, 因为mkfs用于NDIRECT构建文件系统。 - 如果您的文件系统进入错误状态(可能是崩溃),请删除
fs.img(在 Unix 上执行此操作,而不是 xv6)。make将为您构建一个新的干净的文件系统映像。 - 您的每个块
bread()后不要忘记brelse() - 您应该仅根据需要分配间接块和双重间接块,就像原始的
bmap(). - 确保
itrunc释放文件的所有块,包括双间接块。 usertests与之前的实验相比,运行时间更长,因为这个实验FSSIZE更大,而且大文件也更大。
符号链接(中等)
在本练习中,您将添加到 xv6 的符号链接。符号链接(或软链接)通过路径名引用链接文件;当打开符号链接时,内核会跟踪指向所引用文件的链接。符号链接类似于硬链接,但硬链接仅限于指向同一磁盘上的文件,而符号链接可以跨磁盘设备。尽管 xv6 不支持多个设备,但实现此系统调用是了解路径名查找如何工作的一个很好的练习。
你的工作
您将实现symlink(char *target, char *path)系统调用,该系统调用在引用由目标命名的文件的路径中创建一个新的符号链接。有关更多信息,请参阅手册页符号链接。要进行测试,请将 symlinktest 添加到 Makefile 并运行它。当测试产生以下输出(包括成功的用户测试)时,您的解决方案就完成了。
$ symlinktest
Start: test symlinks
test symlinks: ok
Start: test concurrent symlinks
test concurrent symlinks: ok
$ usertests -q
...
ALL TESTS PASSED
$
提示:
- 首先,为符号链接创建一个新的系统调用号,向
user/usys.pl、user/user.h添加一个条目,并在kernel/sysfile.c中实现一个空的sys_symlink。 - 将新文件类型 (
T_SYMLINK) 添加到kernel/stat.h以表示符号链接。 - 向
kernel/fcntl.h添加一个新标志 (O_NOFOLLOW),可与open系统调用一起使用。请注意,传递给的标志open是使用按位或运算符组合的,因此您的新标志不应与任何现有标志重叠。将user/symlinktest.c添加到 Makefile 后,您就可以对其进行编译。 - 实现
symlink(target, path)系统调用以在引用目标的路径处创建新的符号链接。请注意,目标需要不存在才能使系统调用成功。您需要选择某个位置来存储符号链接的目标路径,例如在 inode 的数据块中。symlink应返回一个表示成功 (0) 或失败 (-1) 的整数,类似于link和unlink。 - 修改
open系统调用以处理路径引用符号链接的情况。如果文件不存在,open必然失败。当进程O_NOFOLLOW在标志 to 中指定时open,open应打开符号链接(而不是遵循符号链接)。 - 如果链接的文件也是符号链接,则必须递归地跟踪它,直到到达非链接文件。如果链接形成循环,则必须返回错误代码。如果链接深度达到某个阈值(例如 10),您可以通过返回错误代码来近似这一点。
- 其他系统调用(例如,链接和取消链接)不得遵循符号链接;这些系统调用对符号链接本身进行操作。
- 您不必处理本实验目录的符号链接。
可选的挑战练习
支持三重间接块。
致谢
感谢华盛顿大学 CSEP551(2019 年秋季)的工作人员进行符号链接练习。