2021-xv6实验记录(四)

2021-xv6实验记录(四)

忘记中二的少年 Lv3

本次实验,将进行两个任务

  1. 在 xv6 文件系统的代码里添加对大文件的支持
  2. 实现“符号链接”。

在进行实验前,适当地参考“官方参考书”的第 8 个章节——File System

官方实验指南:https://pdos.csail.mit.edu/6.828/2022/labs/fs.html

官方参考书:https://pdos.csail.mit.edu/6.828/2022/xv6/book-riscv-rev3.pdf

老规矩:实验前先切换分支

1
2
3
4
5
$ git fetch
# 使用上述fetch的时候系统告诉你要缓存或者提交?那你不会commit?
# git commit -m"实验三缓存"
$ git checkout fs
$ make clean

任务一:【Large files】

实验描述

xv6 文件系统的一个“文件”在存储设备(一般为外存)上的逻辑结构图

image-20231113153800593

在XV6底层实现中,文件是由 struct dinode来描述的,其代码结构如下

1
2
3
4
5
6
7
8
struct dinode {
short type; // 文件类型
short major; // 主设备号(当文件类型是设备的时候)
short minor; // 从设备号(当文件类型是设备的时候)
short nlink; // 表示有多少个目录指向该索引节点
uint size; // 文件大小(字节)
uint addrs[NDIRECT + 1]; // 数据块地址
};

根据图中的解释可以清晰的了解到一个文件索引的大小268KB

了解上述基本知识以后,可以尝试在xv6的终端敲入以下命令

image-20231113153852899

1
2
3
4
5
$ bigfile				# 敲入的命令是这条,下面是测试结果
..
wrote 268 blocks
bigfile: file is too small
$

其中,bigfile 是一个用户层的测试程序,它尝试在分配 268 个块之后再分配 65803 个块,很明显,这个数值超过了 xv6 文件系统的现有限制,因此,抛出file is too small的错误。

  • 本次实验的第一个任务已经浮出水面,就是要让 xv6 文件系统支持大文件的创建,以让上述用户层的测试程序能够正常运行。

实验思路

268KB的存储块可以装个啥?装个锤。因此尝试一下在不修改底层dinode结构体大小的情况下

  1. 修改一个之前包含了直接数据块地址的成员变量【就是上述十二个数据块直接地址变量中的一个】,让其指向一个二级间接块
  2. 二级间接块同样有256个条目,但是每个条目指向的是一个一级间接块
  3. 一级间接块又包含256个条目,每个条目指向一个直接数据块

根据我在脑海中生成的样子:

image-20231113153644623

一些提示

  1. kernel/fs.c中的bmap()函数是关键点:主要实现将用户传入的相对的逻辑块号(针对文件的偏移)转为一个绝对的物理块号(针对该设备上的偏移,而一个设备可以包含多个文件);目前bmap仅支持直接块和一级间接块的寻址,你需要添加对二级间接块的寻址
  2. 可以修改一个原本用于“存放直接块地址”的成员变量,让其用于存放二级间接块地址(不能变化 dinode 结构体的大小)
  3. NDIRECT 表示 dinode 里直接块地址的数目,而 NINDIRECT 表示的是 dinode 结构体里间接块地址的数目(都在 kernel/fs.h 里定义),所以针对它们的宏定义,可能需要得到相应的修改。修改了 NDIRECT 的值,请重新生成 xv6虚拟机的镜像文件——fs.img,生成的方法是在 xv6 的源码根目录下敲——make clean,它在清理一些临时的编译文件同时,会在主机系统里强制生成最新的fs.img(这个镜像文件里包含着整个 xv6 虚拟机的文件系统)
  4. 还有一个名为 MAXFILE 的宏定义,表示当前文件系统所支持的数据块数目,因需要支持大文件所以也需要修改。
  5. dinode 是索引节点用于存放在外存上(disk)的结构,其实它在内存中也存在一个特定的版本——名为 inode 结构体(定义在kernel/risc.h),所以,当你为了支持大文件或者二级间接块修改了 dinode 之后,也请修改 inode 的结构体的相应的成员变量
  6. 除了分配块的 bmap()函数,释放块的函数itrunc()也需要修改kernel/fs.c,以支持释放或回收后面加入的二级间接块【可以参考 itrunc 是怎么处理一级间接块的】

实验过程

因为需要将一个直接索引变成二级间接索引,因此根据【提示 2 和 提示 3 】对宏定义进行修改,打开kernel/fs.h文件

1
2
3
4
#define NDIRECT 11 // 减少一个直接索引,增加一个二级间接索引
#define NINDIRECT (BSIZE / sizeof(uint))
#define NDINDIRECT NINDIRECT * NINDIRECT // 二级间接索引提供的块
#define MAXFILE (NDIRECT + NINDIRECT + NBI_INDIRECT)//总数据块的数量

修改 struct dinodestruct inode。【提示 5

打开kernel/fs.h文件修改宏定义以及dinode

因为修改了dinode所以还要修改inode,打开文件kernel/file.h

image-20231113154833044

修改kernel/fs.c文件中的 bmap 函数【提示 1】然后修改itrunc函数【提示 6

image-20231113154555188

结果验证

image-20231113153438723

任务二:【Symbolic links】

实验描述

  • 第二个任务是为 xv6 的文件增加“符号链接”功能
  • 增加一个名为symlink(char *target, char *path)的系统调用,以实现创建符号链接的功能。target 指的是被创建符号链接的文件路径(包括文件名),而path 指的是该符号链接创建后存放的路径名(包括符号链接本身文件名)

实验思路

  1. 创建新系统调用的步骤,为 symlink 提供一个接口/入口,内部 实 现 可 以 暂 时 为 空 ( 需 要 修 改 user/usys.pl、user/user.h 、kernel/sysfile.c 等文件
  2. 在 kernel/stat.h 中,创建一个名为(T_SYMLINK)文件类型,去代表 symlink()所创建的符号链接类型 (xv6 原本支持的文件类型有三种,分别是常规文件、目录文件和设备文件,现在有了第四种)
  3. 在 kernel/fcntl.h 中,加入一个标志位——O_NOFOLLOW,用于 open()在打开符号链接时,以表明打开的对象为符号链接本身,而不是该符号链接所指向的目标文件(没有该标志位的话,系统将无法区分上述两种情况,因为 open 函数的内部处理需要返回一个指向 inode 的指针,那么这个指针到底是应该指向符号链接文件本身,还是其所指向的对象文件?
  4. 可能存在一个嵌套或者递归的情况,你用 open 打开的符号链接,其指向的又是另外一个符号链接……我们最多允许 10次递归,超过就报错

实验过程

  • 注册系统调用symlink【忘记怎么注册?查看实验二】

image-20231113162939169

  • 在文件kernel/sysfile.c文件中实现sys_symlink函数

image-20231113161311951

  • 修改sys_open函数

image-20231113161535075

结果验证

image-20231113163247065

Make Grade测试

  • 需要创建time.txt文件
1
2
touch time.txt
vim time.txt
  • 测试时间因机器的性能不同而有所偏差,大部分情况存在因为虚拟机性能不够导致运行时间过长因此报错

解决方法:打开根目录下的grade-lab-fs文件修改【timeout】的值

image-20231114113527078
  • 标题: 2021-xv6实验记录(四)
  • 作者: 忘记中二的少年
  • 创建于 : 2023-11-14 12:44:00
  • 更新于 : 2023-11-14 13:02:23
  • 链接: https://github.com/HandsomeXianc/HandsomeXianc.github.io/2023/11/14/xv6实验记录(四)/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。