MIT-6.S081 | file system(2021)

137 阅读5分钟

Lab: file system

Large files

​ 实验要求:实现磁盘的二级映射,将磁盘空间由268 blocks扩大为65803 blocks,约为原来的256倍

实现思路:将原来的12个直接映射+1个一级间接映射改为11个直接映射+1个一级间接映射+一个二级映射

//fs.h
#define NDIRECT 11 //注意在进入二级映射之前,记得减掉一个NINDIRECT,保证获取的中间地址号不会多1。
#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
};
//file.h
// 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
static uint
bmap(struct inode *ip, uint bn)
{
  uint addr, *a;
  struct buf *bp;
  // 直接块   block[0-10]
  if(bn < NDIRECT){
    if((addr = ip->addrs[bn]) == 0)
      ip->addrs[bn] = addr = balloc(ip->dev);
    return addr;
  }
  bn -= NDIRECT;
  //s_indirect block[11]  [0-255]
  if(bn < NINDIRECT){
    // Load indirect block, allocating if necessary.
    if((addr = ip->addrs[NDIRECT]) == 0)
      ip->addrs[NDIRECT] = addr = balloc(ip->dev);
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    if((addr = a[bn]) == 0){
      a[bn] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);
    return addr;
  }

  //d_indirect block[12]
  bn -= NINDIRECT;
  if(bn < NINDIRECT*NINDIRECT)
  {
    int mid_addr_num = bn / NINDIRECT; //间接块
    bn = bn % NINDIRECT;
    // Load indirect block, allocating if necessary.
    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[mid_addr_num]) == 0)
    {
      a[mid_addr_num] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);

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

  panic("bmap: out of range");
}

// Truncate inode (discard contents).
// Caller must hold ip->lock.
void
itrunc(struct inode *ip)
{
  int i, j, k;
  struct buf *bp;
  uint *a, *botton_a;
  // 11 direct block[0 ~ 10]  [0 ~ 10]
  for(i = 0; i < NDIRECT; i++){
    if(ip->addrs[i]){
      bfree(ip->dev, ip->addrs[i]);
      ip->addrs[i] = 0;
    }
  }
  // 1 s_direct block[11] => [0 ~ 255]
  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;
  }
  // 1 d_direct block[12] => 256 * [0 ~ 255]
  if(ip->addrs[NDIRECT+1])
  {
    bp = bread(ip->dev, ip->addrs[NDIRECT+1]);
    a = (uint *)bp->data;
    brelse(bp);
    for(j=0; j<NINDIRECT; ++j)
    {
      if(a[j])
      {
        bp = bread(ip->dev, a[j]);
        botton_a = (uint *)bp->data;
        brelse(bp);
        for(k=0; k<NINDIRECT; ++k)
        {
          if(botton_a[k])
            bfree(ip->dev, botton_a[k]);
        }
      }
    }
    bfree(ip->dev, ip->addrs[NDIRECT + 1]);
    ip->addrs[NDIRECT + 1] = 0;
  }
  ip->size = 0;
  iupdate(ip);
}

Symbolic links

实验要求:实现软连接(symboil/soft link)

实现思路:实现syccall: sys_symlink将目标文件inode指针存储到路径文件的inode结构中,之后即可通过路径文件来访问目标文件

具体实现过程中,需要注意路径文件本身可能存在也可能不存在,所以需要检查,根据情况进行创建。这需要处理两个情况,即如果路径文件本身不存在,创建出来后是带锁的,那么自然不需要上锁;但如果路径文件本身就存在,那么它是没有锁的,那么我们就需要使用if(!holdingsleep(&ip->lock))进行检查。

同时,软连接到一个不存在的目标文件是被允许的,也就是说目标文件和路径文件一样,可能存在也可能不存在,所以需要检查,根据情况进行创建。这就会导致一个情况,就是如果带着O_CREATE参数去open()目标文件时,目标文件可能已经在symlink()被创建,如果再创建就是重复创建了,解决方法是进行检查,如果是T_SYMLINK,则不用新创建,改下参数即可;如果不是,则需要将其释放掉,然后再去创建。

open()函数中循环查找时记得解锁,再上锁,保证原子性,查找结束后,需要检查一下目标文件是否真的存在,存在的情况是nlink大于0

无论是目标文件还是路径文件,只要是T_SYMLINK类型,当被unlink()时,就需要执行iput()将其引用减一,让其有机会被释放掉,否则T_SYMLINK类型的inode就会遗留在cache,无法释放,数量一多,就会导致cache中没有空闲的inode可以存储了。

首先是syccall: sys_symlink的注册流程,

//fcnt.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
//file.h
// 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];
  struct inode *sym_link;  //add
};
//stat.h
#define T_DIR     1   // Directory
#define T_FILE    2   // File
#define T_DEVICE  3   // Device
#define T_SYMLINK 4   // symbolic link.

然后是实现sys_symlink

uint64 sys_symlink(void)
{
  char target[MAXPATH], path[MAXPATH];
  struct inode *ip, *ip_target;
  //获得参数target path
  if (argstr(0, target, MAXPATH) < 0 || argstr(1, path, MAXPATH) < 0)
  {
    return -1;
  }
  begin_op();
  // create ip_target if not exist
  if((ip_target = namei(target)) == 0)
  {
    if((ip_target = create(target, T_SYMLINK, 0, 0)) == 0)
    {
      end_op();
      return -1;
    }
  }

  // create ip if not exist
  if((ip = namei(path)) == 0)
  {
    if ((ip = create(path, T_SYMLINK, 0, 0)) == 0)
    {
      end_op();
      return -1;
    }
  }
  // 在本实验中不必处理目录的符号链接。
  if (ip_target)
  {
    if (ip_target->type == T_DIR)
    {
      end_op();
      return -1;
    }
  }

  if (!holdingsleep(&ip->lock))
  {
    ilock(ip);
  }
  ip->sym_link = ip_target;  //在inode中存储指向的文件路径
  iupdate(ip);
  iunlock(ip);
  end_op();
  return 0;
}

open

uint64
sys_open(void)
{
  char path[MAXPATH];
  int fd, omode;
  struct file *f;
  struct inode *ip;
  int n;

  if((n = argstr(0, path, MAXPATH)) < 0 || argint(1, &omode) < 0)
    return -1;

  begin_op();

  if(omode & O_CREATE)
  {
     ip = namei(path);
     if(ip)  //存在
     {
      // if it is T_SYMLINK, we just change it type
      if (ip->type == T_SYMLINK)
      {
        ip->type = T_FILE;
        ip->major = ip->minor = 0;
      }
      else{
        //释放ip,并新建T_FILE类型的
        iput(ip);
        ip = create(path, T_FILE, 0, 0);
        if(ip == 0)
        {
          end_op();
          return -1;
        }
      }
    }
    else{ //不存在新建
      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;
    }
  }
  //检查是否是符号链接
  if(ip->type == T_SYMLINK)
  {
    if (!(omode & O_NOFOLLOW))
    {
      int threshold = 10;
      //循环搜索, 阈值10
      for(int i=0; i<threshold; ++i)
      {
        iunlockput(ip);
        ip = ip->sym_link;
        ilock(ip);
        if(ip->type != T_SYMLINK)
          break;
      }
      // 检查一下目标文件是否真的存在
      if(ip->nlink == 0)
      {
        iunlockput(ip);
        end_op();
        return -1;
      }
    }
  }


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

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

  if(ip->type == T_DEVICE){
    f->type = FD_DEVICE;
    f->major = ip->major;
  } else {
    f->type = FD_INODE;
    f->off = 0;
  }
  f->ip = ip;
  f->readable = !(omode & O_WRONLY);
  f->writable = (omode & O_WRONLY) || (omode & O_RDWR);

  if((omode & O_TRUNC) && ip->type == T_FILE){
    itrunc(ip);
  }

  iunlock(ip);
  end_op();

  return fd;
}

unlink

uint64
sys_unlink(void)
{
  struct inode *ip, *dp;
  struct dirent de;
  char name[DIRSIZ], path[MAXPATH];
  uint off;

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

  begin_op();
  if((dp = nameiparent(path, name)) == 0){
    end_op();
    return -1;
  }

  ilock(dp);

  // Cannot unlink "." or "..".
  if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0)
    goto bad;

  if((ip = dirlookup(dp, name, &off)) == 0)
    goto bad;
  ilock(ip);

  if(ip->nlink < 1)
    panic("unlink: nlink < 1");
  if(ip->type == T_DIR && !isdirempty(ip)){
    iunlockput(ip);
    goto bad;
  }

  memset(&de, 0, sizeof(de));
  if(writei(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))
    panic("unlink: writei");
  if(ip->type == T_DIR){
    dp->nlink--;
    iupdate(dp);
  }
  iunlockput(dp);

  ip->nlink--;
  iupdate(ip);
  iunlockput(ip);

  if(ip->type == T_SYMLINK)
  {
    iput(ip);
  }

  end_op();

  return 0;

bad:
  iunlockput(dp);
  end_op();
  return -1;
}