Lab4文件系统

0. 实验前:切换至Lab2分支

在xv6-labs-2021文件夹下运行终端,并且运行以下命令:

1
2
3
$ git fetch
$ git checkout fs
$ make clean

1. 对 xv6 文件系统添加“大文件”的支持

1.1 Large files

1
Modify bmap() so that it implements a doubly-indirect block, in addition to direct blocks and a singly-indirect block. You'll have to have only 11 direct blocks, rather than 12, to make room for your new doubly-indirect block; you're not allowed to change the size of an on-disk inode. The first 11 elements of ip->addrs[] should be direct blocks; the 12th should be a singly-indirect block (just like the current one); the 13th should be your new doubly-indirect block. You are done with this exercise when bigfile writes 65803 blocks and usertests runs successfully:
1.1.1 解题过程
  • 修改 kernel/fs.h 中的直接块号的宏定义 NDIRECT 为 11;

  • 修改 inode 相关结构体的块号数组,包括 kernel/fs.h 中的磁盘inode 结构体 struct dinodeaddrs ;

    image-20221123212952833

  • kernel/file.h 中的内存inode 结构体 struct inodeaddrs 将数组大小也改变为 NDIRECT+2, 因为 inode 的块号总数没有改变, 但 NDIRECT 减少了 1;

    image-20221123213216833

  • kernel/fs.h添加宏定义二级间接块号总数NDOUBLYINDIRECT,可表示的块号为一级间接块号NINDIRECT的平方;

    image-20221123214404675

  • kernel/fs.cbmap()用于返回inode的相对块号对应的磁盘中的块号,由于 inode 结构中前 NDIRECT 个块号与修改前是一致的, 因此只需要添加对第 NDIRECT 即 13 个块的二级间接索引的处理代码;

    image-20221123214819646

  • 修改kernel/fs.c下的itrunc(),函数用于释放inode的数据块;

    image-20221123215938935

  • 修改kernel/fs.h中的MAXFILE

    image-20221123215633324

1.1.2 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// kernel/fs.h
#define NDIRECT 11
#define NDOUBLYINDIRECT (NINDIRECT * NINDIRECT)
#define MAXFILE (NDIRECT + NINDIRECT + NDOUBLYINDIRECT)
// ...

struct dinode {
// ...
uint addrs[NDIRECT+2];
}

// kernel/file.h

struct inode {
// ...
uint addrs[NDIRECT+2]
}

// kernel/fs.c
static uint
bmap(struct inode *ip, uint bn)
{
// ...
return addr;
}
bn -= NINDIRECT;
if(bn < NDOUBLYINDIRECT) {
if((addr = ip->addrs[NDIRECT + 1]) == 0) {
ip->addrs[NDIRECT + 1] = addr = balloc(ip->dev);
}
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if((addr = a[bn / NINDIRECT]) == 0) {
a[bn / NINDIRECT] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
bn %= NINDIRECT;
if((addr = a[bn]) == 0) {
a[bn] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
return addr;
}

panic("bmap: out of range")
}

void
itrunc(struct inode *ip)
{
int i, j, k;
struct buf *bp, *bp2;
uint *a, *a2;
//...
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]) {
bp2 = bread(ip->dev, a[j]);
a2 = (uint*)bp2->data;
for(k = 0; k < NINDIRECT; ++k) {
if(a2[k]) {
bfree(ip->dev, a2[k]);
}
}
brelse(bp2);
bfree(ip->dev, a[j]);
a[j] = 0;
}
}
brelse(bp);
bfree(ip->dev, ip->addrs[NDIRECT + 1]);
ip->addrs[NDIRECT + 1] = 0;
}

ip->size = 0;
iupdate(ip);
}
1.1.3 运行结果

image-20221123221228205

image-20221123222453492

1
You will implement the symlink(char *target, char *path) system call, which creates a new symbolic link at path that refers to file named by target. For further information, see the man page symlink. To test, add symlinktest to the Makefile and run it. Your solution is complete when the tests produce the following output (including usertests succeeding).
1.2.1 解题过程
  • 分别于kernel/syscall.h, kernel/syscall.c, user/usys.pluser/user.h添加有关symlink系统调用的定义声明。

    image-20221123223225067

    image-20221123223403762

    image-20221123223502076

    image-20221123223600446

  • 添加T_SYMLINKkernel/stat.h

    image-20221123223747774

  • 添加O_NOFOLLOWkernel/fcntl.h

    image-20221123224203974

  • kernel/sysfile.c中实现功能为生成符号链接的sys_symlink()

    image-20221123224754966

    • kernel/fs.h定义NSYMLINK用于表示最大的符号连接深度

    • kernel/sysfile.c中新增函数follow_symlink()

    • 修改kernel/sysfile.csys_open()

      image-20221123225821623

    image-20221123225717349

    image-20221123225758357

1.2.2 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Makefile
$U/_symlinktest\

// kernel/syscall.h
#define SYS_symlink 22

// kernel/syscall.c
extern uint64 sys_symlink(void);
[SYS_symlink] sys_symlink,

// ...
uint64 sys_symlink(void) {
char target[MAXPATH], path[MAXPATH];
struct inode *ip;
int n;

if ((n = argstr(0, target, MAXPATH)) < 0
|| argstr(1, path, MAXPATH) < 0) {
return -1;
}

begin_op();
// create the symlink's inode
if((ip = create(path, T_SYMLINK, 0, 0)) == 0) {
end_op();
return -1;
}
// write the target path to the inode
if(writei(ip, 0, (uint64)target, 0, n) != n) {
iunlockput(ip);
end_op();
return -1;
}

iunlockput(ip);
end_op();
return 0;
}

static struct inode* follow_symlink(struct inode* ip) {
uint inums[NSYMLINK];
int i, j;
char target[MAXPATH];

for(i = 0; i < NSYMLINK; ++i) {
inums[i] = ip->inum;
if(readi(ip, 0, (uint64)target, 0, MAXPATH) <= 0) {
iunlockput(ip);
printf("open_symlink: open symlink failed\n");
return 0;
}
iunlockput(ip);

if((ip = namei(target)) == 0) {
printf("open_symlink: path \"%s\" is not exist\n", target);
return 0;
}
for(j = 0; j <= i; ++j) {
if(ip->inum == inums[j]) {
printf("open_symlink: links form a cycle\n");
return 0;
}
}
ilock(ip);
if(ip->type != T_SYMLINK) {
return ip;
}
}

iunlockput(ip);
printf("open_symlink: the depth of links reaches the limit\n");
return 0;
}

uint64
sys_open(void)
{
// ...

if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
iunlockput(ip);
end_op();
return -1;
}

// handle the symlink - lab9-2
if(ip->type == T_SYMLINK && (omode & O_NOFOLLOW) == 0) {
if((ip = follow_symlink(ip)) == 0) {
end_op();
return -1;
}
}

if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
if(f)
fileclose(f);
iunlockput(ip);
end_op();
return -1;
}

// ...
}

// user/usys.pl
entry("symlink");

// user/user.h
int symlink(char *target, char *path);

// kernel/stat.h
#define T_SYMLINK 4

// kernel/fcntl.h
#define O_NOFOLLOW 0x004

// kernel/fs.h
#define NSYMLINK 10
1.2.3 运行结果

在这里插入图片描述

image-20221123222453492

2.实验心得

​ 非常好奇Large files的$bigfile$usertestswritebig到底是多”big”呢,等了快20分钟他的bigbig file,真的很big。

​ 第二个sys_open函数的修改有点复杂,是否递归或者还是直接打开symlink。文件链接是一个iNode储存的的其实是另一个inode的路径,通过前一个inode找下一个inode,然后再查看另一个inode的值,如果仍是一个inode的路径的话,就继续递归找下去。