Lab:file system

写在前面

文件系统是操作系统对磁盘的抽象,提供相应的API接口,以便于用户使用。在xv6中文件系统一共分为7个层次,如下图所示。最下层是最基础的硬件磁盘,是文件数据直接存储的场所。磁盘具有非易失性的特点,即存储在磁盘上的数据不会因为断电而丢失。磁盘之上是一个缓存,主要作用是缓存常用的磁盘块,从而减少读取磁盘而带来的开销。

缓存之上是日志,其主要作用是为了解决系统崩溃的问题。操作系统中文件操作相关的系统调用通常是由多步的磁盘操作组成的,如果在执行文件系统调用时发生了崩溃,可能会导致磁盘中数据的丢失与错误。日志解决这个问题的基本思想是,通过把文件操作所需要的磁盘操作写入到log块中,只有当所有的磁盘操作都写入完毕后,再提交,并记录磁盘操作所需要的数目;提交完毕后,可以进行真正的磁盘写入操作。当这些操作完成后,将操作计数清零,并清楚日志。如果发生了系统崩溃,重启后若发现操作计数为0,那么相当于文件操作未发生;若操作计数不为0,则重新执行在日志中所有的写入操作。 这样做确保了所有的磁盘操作是原子化的。

日志之上是Inode层。inode记录了文件的硬链接数、文件大小、文件存储的块数以及文件存储的块编号。每次需要创建、读取以及写入文件都需要分配或者读取inode。目录是对inode的进一步抽象,为每一个inode分配了一个目录名。若一个inode有n个目录指向它,那么inode的硬链接数就为n。目录的名字组成了路径名,每个目录名字间用“/”分隔,便于用户通过路径名查找对应的inode。文件描述符则是对路径名进一步的抽象,用户层面主要是通过使用文件描述符来对文件进行操作。

实验部分

一 Large File

xv6中,一个inode总共记录了13个块编号,前12个是直接引用块,后1个是间接引用块。在xv6中一个块编号是4B,一个块是1024B,故一共有1024/4=256个,故总共有268个引用块,因此一个文件最大的大小是268x1KB=268KB。在如今的计算机系统中,268KB无法存储大型的文件,因此需要能够存储更加大的文件。一个办法是将其中一个块编号作为double-indirectly的块,即该块存储256个间接引用块,每个间接引用块可以存储256个文件块,故一共有256*256个引用块,因此一个文件则可以存储(256*256+256+11)个块,即65803个块,大小为65803KB。具体代码如下:

1 在fs.h文件中,将NDIRECT设置为11,将MAXFILE设置为NDIRECT + NINDIRECT+NINDIRECT*NINDIRECT,并在struct dinode中将addrs数组的长度置为NDIRECT+2;以及在file.h中struct inode中addrs数组长度置为NDIRECT+2。

//fs.h

#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];   // Data block addresses
};

//file.h

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

2 bmap中通过块编号访问对应的块,itruct中对块进行释放。对块进行如下编号:

0-10:前11块为直接引用块

11-266:间接引用块

267-65802:double-indirectly块

static uint
bmap(struct inode *ip, uint bn)
{
  uint addr, *a;
  struct buf *bp,*bp2;

  if(bn < NDIRECT){
    if((addr = ip->addrs[bn]) == 0)
      ip->addrs[bn] = addr = balloc(ip->dev);
    return addr;
  }
  bn -= NDIRECT;

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

  bn-=NINDIRECT;
  if(bn<NINDIRECT*NINDIRECT){
    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/256])==0){
      a[bn/256]=addr=balloc(ip->dev);
      log_write(bp);
    }
    bp2=bread(ip->dev,addr);
    a=(uint*)bp2->data;
    if((addr=a[bn%256])==0){
	  a[bn%256]=addr=balloc(ip->dev);
	  log_write(bp2);
    }
    brelse(bp2);
    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;
  struct buf *bp,*bp2;
  uint *a;

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

二 Symbolic links

symbolic links也叫做软链接。硬链接是目录名直接指向对应文件的inode,软链接是将目录名链接到一个额外的inode,但是inode中对应的块存储的是需要软链接的文件的路径名。因此,如果软链接到的文件被删除了,那么就没办法通过软链接打开。

1 创建symlink系统调用相关的代码

//user/usys.pl

entry("symlink");

//user/user.h

int symlink(char*,char*);

//kernel/syscall.c

extern uint64 sys_symlink(void);
[SYS_symlink] sys_symlink,

//kernel/syscall.h

#define SYS_symlink 22

2 添加T_SYMLINK到kernel/stat.h文件中

#define T_SYMLINK 4   // Symbolic link

3 添加O_NOFOLLOW到kernel/fcntl.h中,表示打开一个软链接文件时,不去追溯其源

#define O_NOFOLLOW 0x800

4 实现sys_symlink系统调用。

uint64
sys_symlink(void){
  char target[MAXPATH],path[MAXPATH],name[DIRSIZ];
  struct inode *dp,*sp;
  if(argstr(0,target,MAXPATH)<0 || argstr(1,path,MAXPATH)<0)
    return -1;
  begin_op();
  if((dp=nameiparent(path,name))==0){
    end_op();
    return -1;
  }
  sp=ialloc(dp->dev,T_SYMLINK);
  ilock(sp);
  if(writei(sp,0,(uint64)target,0,sizeof(target))!=sizeof(target)){
    iunlockput(sp);
    iput(dp);
    end_op();
    return -1;
  }
  ilock(dp);
  if(dirlink(dp,name,sp->inum)<0){
    iunlockput(dp);
    iunlockput(sp);
    end_op();
    return -1;
  }
  iunlockput(dp);
  sp->nlink++;
  iupdate(sp);
  iunlockput(sp);
  end_op();
  return 0;
}

5 sys_open系统调用中,当路径名是T_SYMLINK且打开的mode不是O_NOFOLLOW,那么就需要打开路径名软链接到的文件。如果软链接构成了环,则打开文件失败,需要用一个计数器cnt表示软链接的数,若cnt超过10,那么可以认为构成了环。

uint64
getsymlink(struct inode *ip,struct inode **res){
  struct inode *p=ip;
  int cnt=0;
  do{
    char target[MAXPATH];
    readi(p,0,(uint64)target,0,MAXPATH);
    iunlockput(p);
    if((p=namei(target))==0){
      return -1;
    }	    
    cnt++;
    ilock(p);
  }while(cnt<10 && p->type==T_SYMLINK);
  if(cnt==10) return -1;
  *res=p;
  return 0;
}

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 = 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 && omode!=O_NOFOLLOW){
      struct inode *ip2=ip;
      if(getsymlink(ip2,&ip)!=0){
	    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;
}

三 实验结果

 

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值