(undone) MIT6.S081 2023 学习笔记 (Day10: LAB9 fs file system)
url: https://pdos.csail.mit.edu/6.1810/2023/labs/fs.html
任务1:Large files (moderate) ----------------- 完成
本次作业中,你将扩大xv6文件的最大容量。当前xv6文件被限制为268个块(即268*BSIZE字节,xv6中BSIZE为1024)。这一限制源于xv6索引节点(inode)包含12个"直接"块编号和1个"单间接"块编号——后者指向的块可容纳多达256个额外块编号,总计12+256=268个块。
bigfile命令会创建尽可能长的文件,并报告其大小:
$ bigfile
..
wrote 268 blocks
bigfile: file is too small
$
测试失败的原因是 bigfile 期望能创建一个包含 65,803 个块 的文件,但未修改的 xv6 系统将文件大小限制为 268 个块。
你需要修改 xv6 文件系统代码,使其在每个 inode(索引节点) 中支持一个 “双重间接块”(doubly-indirect block),该块包含 256 个单间接块地址,而每个单间接块又可包含最多 256 个数据块地址。这样,一个文件的最大容量将提升至 65,803 个块,即 256×256 + 256 + 11 个块(之所以是 11 而不是 12,是因为我们要牺牲一个直接块编号来存储双重间接块的地址)。
预备知识
mkfs程序负责创建xv6文件系统的磁盘镜像,并决定文件系统的总块数,该大小由kernel/param.h中的FSSIZE控制。在本实验的代码仓库中,FSSIZE被设置为200,000个块。在make的输出中,你应该能看到来自mkfs/mkfs的如下信息:
nmeta 70(引导块、超级块、日志块30、inode块13、位图块25) blocks 199930 total 200000
这行信息描述了mkfs/mkfs构建的文件系统:包含70个元数据块(用于描述文件系统的块)和199,930个数据块,总计200,000个块。
如果在实验过程中需要完全重新构建文件系统,可以运行make clean命令,这会强制make重新构建fs.img。
需要关注的内容
磁盘上inode的格式由fs.h中的struct dinode定义。你需要特别关注其中的NDIRECT、NINDIRECT、MAXFILE以及struct dinode的addrs[]成员。xv6教材中的图8.3展示了标准xv6 inode的结构示意图。
查找文件数据在磁盘上位置的代码位于fs.c中的bmap()函数。请仔细阅读并确保理解其功能。bmap()在读取和写入文件时都会被调用。在写入时,bmap()会根据需要分配新的块来存储文件内容,并在必要时分配间接块来存储块地址。
bmap()处理两种类型的块号:
- 参数bn是"逻辑块号"——即文件内部相对于文件起始位置的块号
- ip->addrs[]中的块号以及bread()的参数是磁盘块号。你可以将bmap()视为将文件的逻辑块号映射到磁盘块号的功能模块。
Your Job
修改bmap()函数,使其除了直接块和单间接块之外,还实现一个双间接块。为了给新的双间接块腾出空间,你需要只有11个直接块,而不是12个;你不允许改变磁盘上inode的大小。ip->addrs[]的前11个元素应该是直接块;第12个应该是单间接块(就像现在的一样);第13个应该是你的新的双间接块。当你完成这个练习时,bigfile应该写入65803个块,并且usertests -q运行成功:
$ bigfile
..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
wrote 65803 blocks
done; ok
$ usertests -q
...
ALL TESTS PASSED
$
bigfile至少需要一分半钟来运行。
提示:
- 确保你理解bmap()。画出ip->addrs[]、间接块、双重间接块以及它指向的单重间接块和数据块之间的关系图。确保你理解为什么添加一个双重间接块会使最大文件大小增加256*256块(实际上是-1,因为你必须将直接块的数量减少一个)。
- 考虑一下你将如何使用逻辑块号索引双重间接块以及它指向的间接块。
- 如果你改变NDIRECT的定义,你可能还需要改变file.h中struct inode的addrs[]的声明。确保struct inode和struct dinode中的addrs[]数组具有相同数量的元素。
- 如果你改变NDIRECT的定义,一定要创建一个新的fs.img,因为mkfs使用NDIRECT来构建文件系统。
- 如果你的文件系统出现问题,可能是由于崩溃,删除fs.img(在Unix中执行此操作,而不是在xv6中)。make将为你构建一个新的干净的文件系统镜像。
- 不要忘记对每个你bread()的块执行brelse()。
- 你应该只在需要时分配间接块和双重间接块,就像原始的bmap()一样。
- 确保itrunc释放文件的所有块,包括双重间接块。
- usertests的运行时间比以前的实验要长,因为在这个实验中FSSIZE更大,大文件也更大。
这个部分的完成过程见于【(done) Lab9 Large files 相关代码解析,以及完成这个 task】
任务2:Symbolic links (moderate) ----------------- doing
在这个练习中,你将为xv6添加符号链接。符号链接(或软链接)通过路径名引用链接文件;当打开一个符号链接时,内核会跟随链接到所引用的文件。符号链接类似于硬链接,但硬链接仅限于指向同一磁盘上的文件,而符号链接可以跨越磁盘设备。虽然xv6不支持多个设备,但实现这个系统调用是一个很好的练习,可以理解路径名查找是如何工作的。
Your job
你将实现symlink(char *target, char *path)系统调用,它在path处创建一个新的符号链接,该链接指向名为target的文件。有关更多信息,请参阅symlink的手册页面。为了测试,请将symlinktest添加到Makefile中并运行它。当测试产生以下输出(包括usertests成功)时,你的解决方案就完成了。
$ symlinktest
Start: test symlinks
test symlinks: ok
Start: test concurrent symlinks
test concurrent symlinks: ok
$ usertests -q
...
ALL TESTS PASSED
$
提示:
- 首先,为symlink创建一个新的系统调用号,向user/usys.pl、user/user.h添加条目,并在kernel/sysfile.c中实现一个空的sys_symlink。
- 向kernel/stat.h添加一个新的文件类型(T_SYMLINK)来表示符号链接。
- 向kernel/fcntl.h添加一个新的标志(O_NOFOLLOW),该标志可以与open系统调用一起使用。请注意,传递给open的标志是使用按位或运算符组合的,因此你的新标志不应与任何现有标志重叠。这将允许你在将其添加到Makefile后编译user/symlinktest.c。
- 实现symlink(target, path)系统调用,以在path处创建一个新的指向target的符号链接。请注意,target不需要存在,系统调用就可以成功。你需要选择一个地方来存储符号链接的目标路径,例如,在inode的数据块中。symlink应该返回一个整数,表示成功(0)或失败(-1),类似于link和unlink。
- 修改open系统调用,以处理路径指向符号链接的情况。如果文件不存在,open必须失败。当进程在传递给open的标志中指定O_NOFOLLOW时,open应该打开symlink(并且不跟随符号链接)。
- 如果链接的文件也是一个符号链接,你必须递归地跟随它,直到达到一个非链接文件。如果链接形成一个循环,你必须返回一个错误代码。你可以通过在链接的深度达到某个阈值(例如10)时返回错误代码来近似这一点。
- 其他系统调用(例如link和unlink)不能跟随符号链接;这些系统调用在符号链接本身上操作。
- 在这个实验中,你不必处理指向目录的符号链接。
TODO: here