MIT6.S081实验九学习记录
lab 9 Filesystem
这个大实验主要做两部分,一个是增加inode的可用“block”,第二个是增加一个符号链接的功能。
这两个实验核心代码不多,但是需要对整体思路有一个非常清晰的把握。
首先讲一下inode,下面的图是表示inode在文件系统中的位置,属于第四层,上面是目录,下面是logging,缓存,硬盘,
然后书里介绍,The inode layer provides individual files, each represented as an inode with a unique i-number and some blocks holding the file’s data. 意思就是说这一层提供了单个的文件,每一个都索引到了具体存放数据的blocks,此时应配合下图理解。
inode里面存了data block的地址(起始地址),映射到了数据实际存储地址,这样通过inode就可以索引到数据块。这也就是为什么后面可以用inode建立软链接的原因,只需要在inode里面放对应目录或者数据的地址即可。
1、Large files
这个实验就是给inode增加间接块的大小,本来的inode是下面这样的。
这里可以看到,从address1 - address12指向了12个数据块,然后通过一个indirect block指向了256个数据块,这样就可以实现一个inode “存储” 268个数据块。这个实验要求将inode改为能够存储256*256+256+11 blocks 的形式,解释起来比较麻烦,直接上个图。
现在大体思路就有了,接下来开始实现。实验指南说要求在bmap上修改,这个bmap是用来返回inode数据块对应磁盘块的。
在此之前,首先需要调整inode原有的一些参数,比如NDIRECT, NINDIRECT, MAXFILE,改成需要的大小。直接块修改为11,最大值改为11+256+256×256,然后把inode的地址块改为直接块+单间接块+双间接块
#define NDIRECT 11
#define NINDIRECT (BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + NINDIRECT + NINDIRECT* NINDIRECT)
struct dinode {
short type; // File type
short major; // Major device number (T_DEVICE only)
short minor; // Minor device number (T_DEVICE only)
short nlink; // Number of links to inode in file system
uint size; // Size of file (bytes)
uint addrs[NDIRECT+2]; // 把这里修改掉
};
static uint
bmap(struct inode *ip, uint bn)
{
uint addr, *a, *b;
struct buf *bp;
struct buf *double_bp;
//当前处于直接块
if(bn < NDIRECT){ //Is bn a direct block?
if((addr = ip->addrs[bn]) == 0)
ip->addrs[bn] = addr = balloc(ip->dev);
return addr;
}
//向下减去直接块的大小
bn -= NDIRECT;
//当前处于单间接块,注意索引从0开始,所以NDIRECT指的是第一个单间接块
if(bn < NINDIRECT){
// Load indirect block, allocating if necessary.
if((addr = ip->addrs[NDIRECT]) == 0) //inode is not exited
ip->addrs[NDIRECT] = addr = balloc(ip->dev);
bp = bread(ip->dev, addr); //read this buf from disk
a = (uint*)bp->data; //first pointer of data
if((addr = a[bn]) == 0){ //need to allocate
a[bn] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
return addr;
}
bn -= NINDIRECT;
//当前处于双间接块,注意索引从0开始,所以NDIRECT + 1指的是第一个双间接块
if(bn < NINDIRECT * NINDIRECT){
if((addr = ip->addrs[NDIRECT + 1]) == 0) //双间接块是否存在,没有就分配
ip->addrs[NDIRECT + 1] = addr = balloc(ip->dev);
double_bp = bread(ip->dev, addr); //read this buf from disk
a = (uint*)double_bp->data; //first pointer of data
if((addr = a[bn/NINDIRECT]) == 0){ //need to allocate
a[bn/NINDIRECT] = addr = balloc(ip->dev); //每隔256分配一个起始地址
log_write(double_bp);
}
brelse(double_bp);
//分配256*256块
bn = bn % NINDIRECT;
double_bp = bread(ip->dev, addr); //read this buf from disk
b = (uint*)double_bp->data; //first pointer of data
if((addr = b[bn]) == 0){ //need to allocate
b[bn] = addr = balloc(ip->dev); //将上面256个起始地址块之间填满256
log_write(double_bp);
}
brelse(double_bp);
return addr;
}
//bn is out of range MAXFILE
panic("bmap: out of range");
}
然后还需要修改itrunc 函数,既然增加映射添加了,那么删除映射也要添加。
void
itrunc(struct inode *ip)
{
int i, j, k;
struct buf *bp, *double_bp;
uint *a, *b;
for(i = 0; i < NDIRECT; i++){
if(ip->addrs[i]){
bfree(ip->dev, ip->addrs[i]);
ip->addrs[i] = 0;
}
}
if(ip->addrs[NDIRECT]){
bp = bread(ip->dev, ip->addrs[NDIRECT]);
a = (uint*)bp->data;
for(j = 0; j < NINDIRECT; j++){
if(a[j])
bfree(ip->dev, a[j]);
}
brelse(bp);
bfree(ip->dev, ip->addrs[NDIRECT]);
ip->addrs[NDIRECT] = 0;
}
//仿照上面的写法,按起始地址块,循环删除即可
if(ip->addrs[NDIRECT + 1]){
bp = bread(ip->dev, ip->addrs[NDIRECT + 1]);
a = (uint*)bp->data;
for(j = 0; j < NINDIRECT; j++){
if(a[j]){
double_bp = bread(ip->dev, a[j]); //找到第j个双间接块
b = (uint*)double_bp->data;
for (k = 0; k < NINDIRECT; k++) { //循环删除第j个双间接块的数据
if (b[k])
bfree(ip->dev, b[k]);
}
brelse(double_bp);
bfree(ip->dev, a[j]);
}
}
brelse(bp);
bfree(ip->dev, ip->addrs[NDIRECT + 1]);
ip->addrs[NDIRECT + 1] = 0;
}
ip->size = 0;
iupdate(ip);
}
最后编译测试一下bigfile。
jimmy@ubuntu:~/xv6-test/xv6-labs-2020$ make qemu
qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 1 -nographic -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
xv6 kernel is booting
init: starting sh
$ bigfile
..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
wrote 65803 blocks
bigfile done; ok
2、Symbolic links
对我来说,这个实验比上一个理解起来难度要大很多,看了教材和实验要求后,也是参考了很多博主的博客才大概理解了符号链接的含义。
总体来说,就是要求建立一系列的inode,里面存放了链接的文件或者目录,因此可以通过这些inode来快速访问需要的文件或目录,用T_SYMLINK来标志这个inode是符号链接的inode。但是一旦访问这个节点就转去符号链接的话,明显不合理,因为有可能还要对当前的文件或目录进行操作,因为还需要加上一个标志位O_NOFOLLOW,来表明是否要跟随当前的符号链接到更下一级的文件或目录。
## /kernel/stat.h
#define T_DIR 1 // Directory
#define T_FILE 2 // File
#define T_DEVICE 3 // Device
#define T_SYMLINK 4 //SYMLINK
## /kernel/fcntl.h
#define O_RDONLY 0x000
#define O_WRONLY 0x001
#define O_RDWR 0x002
#define O_CREATE 0x200
#define O_TRUNC 0x400
#define O_NOFOLLOW 0x800 //NOFOLLOW flag
然后需要将其封装为一个系统调用,这一步类似之前的实验二,在user态添加了入口后,需要在kernel态的sysfile.c 添加实现。
按照上面的理解,这个函数有两个参数,需要把target 放到path中,然后创建一个inode,将其flag设置为T_SYMLINK,然后把target写入这个inode即可。仿照link,ulink的一些写法,代码如下。
uint64
sys_symlink(void)
{
char target[MAXPATH], path[MAXPATH];
struct inode *ip;
if(argstr(0, path, MAXPATH) < 0 || argstr(1, target, MAXPATH) < 0)
return -1;
begin_op();
//this path already exits
if((ip = namei(path)) != 0){
end_op();
return -1;
}
ip = create(path, T_SYMLINK, 0 , 0);
if(ip == 0){
end_op();
return -1;
}
if((writei(ip, 0, (uint64)target, 0, MAXPATH)) < 0){
end_op();
return -1;
}
iunlockput(ip);
end_op();
return 0;
}
然后需要修改sys_open,加上判断当前inode是否是T_SYMLINK而且可以跟随,如果满足条件就一直递归到下一级路径。但是如果递归次数超过一个阈值,就认为进入死循环,就直接返回-1即可。
uint64
sys_open(void)
{
......
begin_op();
if(omode & O_CREATE){
ip = create(path, T_FILE, 0, 0);
if(ip == 0){
end_op();
return -1;
}
} else {
if((ip = namei(path)) == 0){
end_op();
return -1;
}
ilock(ip);
if(ip->type == T_DIR && omode != O_RDONLY){
iunlockput(ip);
end_op();
return -1;
}
int depth = 0;
while(ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)){
//读取当前inode里的路径,并且放到path中(下一级路径)
if(readi(ip, 0 , (uint64)path, 0, MAXPATH) < 0){
iunlockput(ip);
end_op();
return -1;
}
iunlockput(ip);
//将当前ip指向下一级的路径
if((ip = namei(path)) == 0){ //point the next path
end_op();
return -1;
}
ilock(ip);
//已经递归超过十次,直接退出
if(++depth > 10){
iunlockput(ip);
end_op();
return -1;
}
}
}
......
}
然后编译一下,看看结果。
== Test running bigfile ==
$ make qemu-gdb
running bigfile: OK (154.2s)
(Old xv6.out.bigfile failure log removed)
== Test running symlinktest ==
$ make qemu-gdb
(0.4s)
== Test symlinktest: symlinks ==
symlinktest: symlinks: FAIL
...
Start: test symlinks
FAILURE: failed to stat b
Start: test concurrent symlinks
test concurrent symlinks: ok
$ qemu-system-riscv64: terminating on signal 15 from pid 4249 (make)
MISSING '^test symlinks: ok$'
== Test symlinktest: concurrent symlinks ==
symlinktest: concurrent symlinks: OK
== Test usertests ==
$ make qemu-gdb
usertests: OK (236.1s)
(Old xv6.out.usertests failure log removed)
== Test time ==
time: OK
Score: 80/100
发现这里有一个用例一直没通过。然后debug发现是写系统调用时,把symlink的返回值类型写错了,而且把参数列表填反了。。。一定要细心啊!
## /kernel/sysfile.c/sys_symlink
//第一个参数是target,第二个是path
if(argstr(0, path, MAXPATH) < 0 || argstr(1, target, MAXPATH) < 0)
return -1;
这里第一个参数应该是target,第二个是path。修改后,再次编译发现全部通过。
jimmy@ubuntu:~/xv6-test/xv6-labs-2020$ ./grade-lab-fs
make: 'kernel/kernel' is up to date.
== Test running bigfile == running bigfile: OK (143.5s)
== Test running symlinktest == (0.7s)
== Test symlinktest: symlinks ==
symlinktest: symlinks: OK
== Test symlinktest: concurrent symlinks ==
symlinktest: concurrent symlinks: OK
== Test usertests == usertests: OK (243.7s)
== Test time ==
time: OK
Score: 100/100
总结
这个实验重在理解文件系统的结构,然后改写了inode的数据块索引,编写了软链接的系统调用,总的来说难度还是有的,需要对整体内容做深刻把握。
[1]. https://pdos.csail.mit.edu/6.S081/2020/labs/fs.html
[2]. https://zhuanlan.zhihu.com/p/630222721