前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >6.S081/6.828: 9 Lab file system

6.S081/6.828: 9 Lab file system

原创
作者头像
冰寒火
发布2022-12-02 00:30:06
4050
发布2022-12-02 00:30:06
举报
文章被收录于专栏:软件设计软件设计

参考xv6源码分析--文件系统。

一、Large files

1 目的

目前xv6包含12个直接索引和1个二级索引,共索引12KB+256KB。增加xv6文件的索引范围,使得能够索引256*256KB+256KB+11KB,牺牲一个直接索引,将其转变为三级索引。

2 问题分析

mkfs程序会创建xv6文件系统磁盘镜像并决定文件系统有多少blocks,这个大小受kernel/param.h中的FSSIZE控制。

文件系统启动
文件系统启动

访问索引的场景:

  1. 读写block时需要根据文件偏移量从索引块中获取磁盘block编号,bmap函数;
  2. 清空文件时需要遍历索引块并释放掉磁盘空间,itrunc函数。

3 代码实现

读写block时调用bmap获取磁盘块地址,

  1. 如果在0, 10,那么就读取直接索引;
  2. 如果在11, 266,那么就先读取single-indirect索引块,然后读取指定索引项;
  3. 如果在267, 65803,那么就读取两级索引块,再根据偏移量获取索引项。
代码语言:c
复制
//返回该偏移量的block地址
static uint
bmap(struct inode *ip, uint bn)
{
  uint addr, *a;
  struct buf *bp;
  //从直接索引读取blockno
  if(bn < NDIRECT){
    if((addr = ip->addrs[bn]) == 0)
      ip->addrs[bn] = addr = balloc(ip->dev);
    return addr;
  }
  bn -= NDIRECT;
  //从二级索引中读取blockno
  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 < NDBINDIRECT){
    // 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;
    //分配二级目录
    int mediumindex=bn/NINDIRECT;
    if((addr = a[mediumindex]) == 0){
      a[mediumindex] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);
    //分配一级目录
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    int bottomindex=bn%NINDIRECT;
    if((addr = a[bottomindex]) == 0){
      a[bottomindex] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);
    // printf("三级索引,bn[%d],addr[%d]\n",bn,addr);
    return addr;
  }

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

释放文件空间时也需要访问索引并释放掉所有block,依次释放直接索引指向的block,single-indirect指向的block以及double-indirect指向的block,并将索引块也释放掉,设置size=0。

代码语言:c
复制
// Truncate inode (discard contents).
// Caller must hold ip->lock.
void
itrunc(struct inode *ip)
{
  int i, j;
  struct buf *bp;
  uint *a;
  //释放直接索引的block
  for(i = 0; i < NDIRECT; i++){
    if(ip->addrs[i]){
      bfree(ip->dev, ip->addrs[i]);
      ip->addrs[i] = 0;
    }
  }
  //释放间接索引的block
  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;
  }
  //释放二级间接索引
  //释放间接索引的block
  if(ip->addrs[NDIRECT+1]){
    bp = bread(ip->dev, ip->addrs[NDIRECT+1]);
    a = (uint*)bp->data;
    struct buf* bbp;
    uint *aa;
    for(j = 0; j < NINDIRECT; j++){
      if(0==a[j])
        continue;
      bbp=bread(ip->dev,a[j]);
      aa=(uint*)bbp->data;
      for(int i=0;i< NINDIRECT;i++){
        if(aa[i])
          bfree(ip->dev,aa[i]);
      } 
      brelse(bbp);
    }

    brelse(bp);
    bfree(ip->dev, ip->addrs[NDIRECT+1]);
    ip->addrs[NDIRECT+1] = 0;
  }

  ip->size = 0;
  //更新磁盘上inode
  iupdate(ip);
}

二、Symbolic links

1 目的

本任务是实现symlink(char *target, char *path)系统调用,给xv6添加符号链接。符号链接是一个特殊文件,保存了被链接文件的path。硬链接是创建新的目录项指向已存在文件的inode,但是只能指向磁盘上文件,而符号链接更加灵活,能指向设备文件。

2 问题分析

符号链接文件和普通文件的读写、打开、创建不一样,如下:

  1. 符号链接是从文件内容读取path,然后根据dirlookup查找目标文件的inode,与目录、普通文件不一样,所以需要定义新的文件类型T_SYMLINK
  2. 添加symlink系统调用,它会调用create创建符号链接文件,这个和普通文件的创建有所不同;
  3. 添加一个flag O_NOFOLLOW,使得能够打开符号文件,如果被链接的文件也是一个符号链接文件,那就需要递归的查找直到遇到普通文件为止,如果链接关系是一个环,则需要error,可以根据递归深度来决定;
  4. 其他的系统调用打开符号链接文件就是操作符号链接文件本身,不需要查找被链接的文件。

3 代码实现

首先增加系统调用symlink;

代码语言:c
复制
//syscall.h
#define SYS_symlink  22

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

//syscall.c
[SYS_symlink]   sys_symlink,

//stat.h
#define T_SYMLINK 4   //symbolic link

然后实现sys_symlink(),如下:

  1. 读取target字符串和path地址;
  2. 查找path是否存在,如果不存在就创建该符号文件;
  3. 向该文件写入target。
代码语言:c
复制
uint64
sys_symlink(void){

  char target[MAXPATH],path[MAXPATH];
  if(argstr(0,target,MAXPATH)<0)
    return -1;
  if(argstr(1,path,MAXPATH)<0)
    return -1;
  struct inode *ip;
  struct file *f;
  int fd;

  begin_op();
  //查找该path的文件inode是否存在
  if((ip = namei(path)) == 0){
    ip=create(target,T_SYMLINK,0,0);
    if(ip==0){
      end_op();
      return -1;
    }
  }else{
    ilock(ip);
  }
  if(writei(ip,0,target,ip->size,MAXPATH)!=MAXPATH){
    panic("panic: symlink");
  }
  iunlockput(ip);
  end_op();
  return 0;
}

打开符号链接文件时要区分,以普通文件形式打开来读写符号链接文件,还是以O_NOFOLLOW=0模式打开读取被链接的普通文件。如果是访问被链接的文件,那么就递归的查找inode,通过readi从inode中读取数据symbolic path,然后读取symbolicpath inode并判断是不是T_SYMLINK,如果是就继续递归。为了防止出现环,最多递归10次,超过就认为有环。

代码语言:c
复制
if(ip->type==T_SYMLINK && (omode & O_NOFOLLOW)==0){
      int count=0;
      char symlinkpath[MAXPATH];
      while(1){
        if(count>10){
          iunlockput(ip);
          end_op();
          return -1;
        }
        if(readi(ip,0,(uint64)symlinkpath,ip->size-MAXPATH,MAXPATH)!=MAXPATH){
          panic("panic: symlink");
        }
        iunlockput(ip);
        if((ip=namei(symlinkpath))==0){
          end_op();
          return -1;
        }
        ilock(ip);
        if(ip->type!=T_SYMLINK){
          break;
        }
        count++;
      }
    }

测试结果

测试结果
测试结果

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Large files
    • 1 目的
      • 2 问题分析
        • 3 代码实现
        • 二、Symbolic links
          • 1 目的
            • 2 问题分析
              • 3 代码实现
              • 测试结果
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档