【XV6】 file system

代码:https://github.com/JasenChao/xv6-labs.git

支持大文件

XV6目前只支持268个blocks大小的文件,一个block(BSIZE)为1024,文件块inode包含12个一级地址和1个二级地址,二级地址指向另一个block,其中存放了256个一级地址,因此一共是268个。

题目要求支持大文件(65803个blocks),提示通过三级地址来实现,将一级地址调整为11个,多出来的一个指向三级地址(三级地址中包含256个二级地址),最大就可以支持256*256+256+11=65803个blocks大小的文件。

首先在kernel/fs.h中把一级地址的数量从12改为11,NINDIRECT是256,那么新定义NDINDIRECT为256*256,是三级地址可以包含的blocks数量,从而MAXFILE应为NDIRECT + NINDIRECT + NDINDIRECT,结构体inodedinode也需要修改使得包含三级地址:

#define NDIRECT 11
#define NINDIRECT (BSIZE / sizeof(uint))
#define NDINDIRECT (NINDIRECT * NINDIRECT)
#define MAXFILE (NDIRECT + NINDIRECT + NDINDIRECT)

// 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
};

// in-memory copy of an inode
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+2];
};

修改fs.c中的bmap函数,函数中只包含了一级和二级地址的处理过程,仿照完成三级地址的部分,加在二级地址的代码后面:

 bn -= NINDIRECT;

  if(bn < NDINDIRECT){
    // Load indirect block, allocating if necessary.
    if((addr = ip->addrs[NDIRECT + 1]) == 0){
      addr = balloc(ip->dev);
      if(addr == 0)
        return 0;
      ip->addrs[NDIRECT + 1] = addr;
    }
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    uint c = bn / NINDIRECT;
    bn %= NINDIRECT;
    if((addr = a[c]) == 0){
      addr = balloc(ip->dev);
      if(addr){
        a[c] = addr;
        log_write(bp);
      }
    }
    brelse(bp);

    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    if((addr = a[bn]) == 0){
      addr = balloc(ip->dev);
      if(addr){
        a[bn] = addr;
        log_write(bp);
      }
    }
    brelse(bp);
    return addr;
  }

对应地,释放文件的itrunc函数也要增加对于三级地址的处理:

 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]){
        struct buf *bp1 = bread(ip->dev, a[j]);
        uint *a1 = (uint*)bp1->data;
        for(i = 0; i < NINDIRECT; ++i)
          if(a1[i])
            bfree(ip->dev, a1[i]);
        brelse(bp1);
        bfree(ip->dev, a[j]);
        a[j] = 0;
      }
    }
    brelse(bp);
    bfree(ip->dev, ip->addrs[NDIRECT + 1]);
    ip->addrs[NDIRECT + 1] = 0;
  }

通过make GRADEFLAGS=bigfile grade测试代码是否通过。

支持符号链接

题目要求实现一个系统调用symlink创建符号链接,新增系统调用之前做过很多次:

# user/user.h
int symlink(const char*, const char*);

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

# kernel/syscall.h
#define SYS_symlink 22

# kernel/syscall.c
extern uint64 sys_symlink(void);

static uint64 (*syscalls[])(void) = {
...
[SYS_symlink]   sys_symlink,
};

根据提示在kernel/stat.h中增加新的文件类型:

#define T_SYMLINK 4

之前已经创建了系统调用symlink,但是还没有实现具体的函数,在kernel/sysfile.c中实现,先创建一个inode,然后将目标文件的路径写入其中第一块block,其中需要注意creat会对申请的inode上锁但不会解锁:

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

  if(argstr(0, target, MAXPATH) < 0 || argstr(1, path, MAXPATH) < 0) return -1;

  begin_op();

  if((ip = create(path, T_SYMLINK, 0, 0)) == 0){
    end_op();
    return -1;
  }

  if(writei(ip, 0, (uint64)target, 0, strlen(target)) < 0){
    iunlockput(ip);
    end_op();
    return -1;
  }

  iunlockput(ip);
  end_op();

  return 0;
}

根据提示在kernel/fcntl.h中增加新的flag,用于系统调用open,这里的标志位是按位运算的,不要与已经定义的标志重叠:

#define O_NOFOLLOW 0x800

Makefile中加入已经提供的测试程序:

UPROGS=\
	$U/_cat\
	$U/_echo\
	$U/_forktest\
	$U/_grep\
	$U/_init\
	$U/_kill\
	$U/_ln\
	$U/_ls\
	$U/_mkdir\
	$U/_rm\
	$U/_sh\
	$U/_stressfs\
	$U/_usertests\
	$U/_grind\
	$U/_wc\
	$U/_zombie\
	$U/_symlinktest\

修改kernel/sysfile.c中的sys_open函数,增加对符号链接文件类型的处理,循环读取inode,直到找到目标文件或者达到深度为10为止:

int depth = 0;
while(!(omode & O_NOFOLLOW) && ip->type == T_SYMLINK)
{
  if(readi(ip, 0, (uint64)path, 0, MAXPATH) == -1)
  {
    iunlockput(ip);
    end_op();
    return -1;
  }
  iunlockput(ip);
  if((ip = namei(path)) == 0)
  {
    end_op();
    return -1;
  }
  
  if(++depth == 10)
  {
    end_op();
    return -1;
  }
  ilock(ip);
}

通过make GRADEFLAGS=symlinktest grade测试代码是否通过。

测试结果

使用make grade测试,结果如下:

== Test running bigfile == 
$ make qemu-gdb
running bigfile: OK (53.6s) 
== Test running symlinktest == 
$ make qemu-gdb
(0.5s) 
== Test   symlinktest: symlinks == 
  symlinktest: symlinks: OK 
== Test   symlinktest: concurrent symlinks == 
  symlinktest: concurrent symlinks: OK 
== Test usertests == 
$ make qemu-gdb
usertests: OK (100.7s) 
posted on 2024-02-14 19:06  未连接到互联网  阅读(17)  评论(0编辑  收藏  举报