0%

xv6-2020-lab9解析

先导知识

xv6的文件系统设计的层次是这样子的:

1
2
3
4
5
6
7
system calls
name ops | FD ops
inodes
inode cache
log
buffer cache
virtio_disk driver

磁盘本身是按照扇区作为基本单位的,一个扇区是512字节。绝大部分的操作系统一页是4096字节,所以是8个扇区;但是xv6的一个block是2个扇区。

xv6的磁盘的布局是这样子的:

1
2
3
4
5
6
7
0: unused
1: super block (size, ninodes)
2: log for transactions
32: array of inodes, packed into blocks
45: block in-use bitmap (0=free, 1=used)
46: file/dir content blocks
end of disk

每一个inode的结构是这个样子的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type (free, file, directory, device)
nlink
size
addrs[12+1]

------------------------------------------------------

struct inode {
uint dev; // Device number
uint inum; // Inode number
int ref; // Reference count
struct sleeplock lock; // protects everything below here
int valid; // inode has been read from disk?

short type; // copy of disk inode
short major;
short minor;
short nlink;
uint size;
uint addrs[NDIRECT+1];
};

其中一个inode的大小是64字节。这里可能仔细算一算会发现,上面的inode明显超过了64字节,这是因为上面的这个inode并不是存放在磁盘上的,而是存放在内存之中的。存放在磁盘上对应的数据结构是dinode:

1
2
3
4
5
6
7
8
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+1]; // Data block addresses
};

short = 4字节,uint = 8字节,正好64字节。

当执行一个echo hi > x ,可以简单追踪一下对于磁盘来说发生了什么,其实主要是三个步骤,分别是创建x这个文件、往x里面写入文件、更新对应的inode。具体步骤如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// create
bwrite: block 33 by ialloc // allocate inode in inode block 33
bwrite: block 33 by iupdate // update inode (e.g., set nlink)
bwrite: block 46 by writei // write directory entry, adding "x" by dirlink()
bwrite: block 32 by iupdate // update directory inode, because inode may have changed
bwrite: block 33 by iupdate // itrunc new inode (even though nothing changed)
// write
bwrite: block 45 by balloc // allocate a block in bitmap block 45
bwrite: block 595 by bzero // zero the allocated block (block 524)
bwrite: block 595 by writei // write to it (hi)
bwrite: block 33 by iupdate // update inode
// write
bwrite: block 595 by writei // write to it (\n)
bwrite: block 33 by iupdate // update inode

首先是在33号inode中分配了对应的inode,并且更新了这个inode(挺奇怪为什么不是在同一个write来完成的)然后在当前的目录中新增一个文件x,即在direntry中新增这个文件名和inode之间的映射。然后还需要去访问对应的父节点的inode进行修改。

创建好inode之后就可以就去对应的数据块中进行数据的写入了?还首先需要去bitmap中声明该数据块被使用了,然后就可以去对应的数据块进行读取了。

第三部分其实是因为,echo本身还会往最后加入一个\n,所以还有一次write。

Large files(中等)

在原版xv6中,由于一个iNode只有13个数据block,其中一个是间接区(256个),另外12个是直接区,一共是268个block,一个block在xv6中是1K,所以原版xv6只能支持最大268KB的文件。

有一个测试程序bigfile,能够检测最大的文件能够到多大,要达到65803才可以。

一共13个block指针,那么11个作为直接指针,1个作为一级的间接区,1个作为二级的间接区。这样就是 11 + 1*256 + 1 * 256 * 256 = 65803刚刚好。

Preliminaries

最开始的文件系统是由mkfs来构造的,而原始的xv6一共是200000个blocks,这个其实是由kernel/param.h中的FSSIZE来进行控制的。

What to Look At

在磁盘上的iNode数据对应的数据结构是struct dinode,除此之外比较重要的代码是fs.c中的bmap(),确保你了解这个函数在做什么,

Your Job

把其中的一个直接块改成双间接层的块。

hints:

  • 确保你了解了bmap,推荐画一个关系图。
  • 思考一下如何索引这些间接块和二级间接块。
  • 如果你修改了NDIRECT,记得删除fs.img然后重新构造一个。
  • 对于任何bread,不要忘记使用brelse
  • 确保itrunc释放所有的块。

修改常量

根据上面的计算,我们应该把直接的块映射改成11。并且修改对应的数据结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
#define NDIRECT 11
#define NINDIRECT (BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + NINDIRECT + NINDIRECT * NINDIRECT)

// On-disk inode structure
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]; // Data block addresses
};

同理还有inode别忘记也修改。

修改bmap