6.s081 学习实验记录(十)file system

首先切换到 fs 分支

  • $ git fetch
  • $ git checkout fs
  • $ make clean

一、Large files

简介

该实验需要我们增大xv6文件的最大大小,目前xv6的最大文件大小为 268个块,即 268 * BSIZE。这是因为 xv6 包含12个直接块号 和 一个一级间接块号最大索引 12 + 256 = 268 个块

bigfile 命令将创建xv6允许的最大文件,并报告其大小,未进行实验前结果如图:
在这里插入图片描述
我们需要让xv6支持二级间接块,这样支持的索引块号最大为 11 + 256 + 256 * 256 = 65803 个,这样即可满足 bigfile 的测试要求。

我们的任务:修改 bmap() 函数让其可以支持二重间接块号,

提示

  • 磁盘文件的inode定义由 fs.hstruct inode 定义
  • 主目录中的 mkfs 程序用于创建 xv6 文件系统磁盘硬件并确定该磁盘总共多少数据块,磁盘的总大小由 kernel/param.h 中的FSSIZE 控制,当前分支已经将磁盘的大小改为 20w 个块大小
  • 在磁盘上查找文件数据的函数是 fs.cbmap(),阅读它并确保知道该函数在做什么。该函数在读取和写入文件时都会被调用,写入时bmap()根据需要分配新的块来保存内容,并根据需要分配间接块来保存块地址。
  • 可以将 bmap() 函数理解为将文件的逻辑块号映射为磁盘的物理块号,该函数的 bn 参数表示逻辑块号。而 ip->addrs[] 数组下标和 bread() 需要的都是物理块号,因此 bmap() 需要将 bn 转换为物理块号
  • 如果修改NDIRECT的定义,应该同时修改 file.hstruct inodeaddrs[]数组的大小,需要让他们保证元素数目一致
  • 不要忘记在 bread() 后对读取的块使用 brelse() 函数
  • 确保 itrunc() 可以释放所有块,包括二级间接块

实验代码

本实验中磁盘的最大已经改为20w,因此无需通过mkfs重新创建磁盘。我们只需要修改bmap()即可。

  • fs.h
#define NDIRECT 11
#define ONE_LEVEL_INDIRECT_IDX 11
#define TWO_LEVEL_INDIRECT_IDX 12
#define NINDIRECT (BSIZE / sizeof(uint))
#define N_TWO_INDIRECT NINDIRECT * NINDIRECT
#define MAXFILE (NDIRECT + NINDIRECT + N_TWO_INDIRECT)

// 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
};
  • fs.c
struct {
  struct spinlock lock;
  struct inode inode[TWO_LEVEL_INDIRECT_IDX];
} itable;



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

  // 如果是直接块
  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 < N_TWO_INDIRECT){
    // 加载二级间接索引块,如果二级索引块还未分配,则分配一个数据块作为二级索引块
    // 如果二级索引块不存在,为其分配地址,并记录在 addrs 数组对应的位置
    if((addr = ip->addrs[TWO_LEVEL_INDIRECT_IDX]) == 0){
      ip->addrs[TWO_LEVEL_INDIRECT_IDX] = addr = balloc(ip->dev);
    }
    // 读取二级索引块,内容为一级索引块地址
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    // bn 必定是可以整除 NINDIRECT 的数,决定查询一级索引的第几项,如果此时一级索引块也未分配
    // 分配一级索引块,并将地址记录到对应的二级索引块中
    if((addr = a[bn / NINDIRECT]) == 0){
      a[bn / NINDIRECT] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);
    // 读取一级索引块的内容
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    // 得到在一级索引块中实际数据块的idx,如果不存在,则分配
    if((addr = a[bn % NINDIRECT]) == 0){
      a[bn % NINDIRECT] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);
    return addr;
  }

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

实验结果

  • usertests -q(该程序至少需要运行1分半)
    在这里插入图片描述
  • userstest -q
    在这里插入图片描述

二、Symbolic links

简介

实现符号链接(软链接)
目标:实现 symlink(char *target, char *path) 系统调用,该系统调用在path表示路径下创建一个新的符号链接,引用target路径。最终通过symlinktest测试。

提示

  • 创建一个新的系统调用 symlink,在 user/usys.pl, user/user.h添加对应的项,在 kernel/sysfile.c 中实现sys_symlink()函数
  • kernel/stat.h 中添加T_SYMLINK文件类型,表示符号链接类型的文件
  • kernel/fcntl.h添加一个新的flag标志位O_NOFOLLOW,该标记位将在 open() 系统调用中使用。注意flag标记位在open() 中使用时需要使用按位或(or)
  • 实现 symlink 系统调用的逻辑,需要在一个地方先缓存 target 路径,可以选择存储在 inode的数据块中,同时该系统调用应该在成功时返回0,失败时返回-1
  • 修改 open()系统调用,在打开一个符号链接文件时,如果其引用的对象不存在,则打开失败,如果存在,则打开其引用的对象
  • 如果符号链接文件引用的文件依然是符号链接文件,那么需要递归的去引用,直到遇到一个非链接文件。如果递归变为循环递归,需要返回错误,且需要添加一个递归阈值(例如10)以防止无限制递归。
  • 其他系统调用,操作符号链接文件本身时,就应该操作该文件本身(例如 link、unlink)。
  • 无需处理对于目录的符号链接

实验代码

  • kernel/fcntl.h
#define O_NOFOLLOW 0x004
  • kernel/stat.h
#define T_SYMLINK  4  // symbol link
  • user/user.h
int symlink(char *target, char *path);
  • user/usys.pl
entry("symlink");
  • makefile
$U/_symlinktest\
  • kernel/syscall.h
#define SYS_symlink  22
  • kernel/syscall.c
extern uint64 sys_symlink(void);

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


  • kernel/sysfile.c:sys_symlink 可以参考 sys_link
uint64
sys_symlink(void){
  char target[MAXPATH];
  char path[MAXPATH];
  struct inode *ip;

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

  begin_op();

  // 如果是目录,返回错误
  if((ip = namei(target)) != 0){
    ilock(ip);
    if(ip->type == T_DIR){
      iunlock(ip);
      end_op();
      return -1;
    }
    iunlock(ip);
  }

  // 创建软链接文件,返回的是一个上锁的fd
  ip = create(path, T_SYMLINK, 0, 0);
  if(ip == 0){
      end_op();
      return -1;
  }

  // 将 target 路径写入到软链接文件中
  if(writei(ip, 0, (uint64)target, 0, strlen(target) + 1) <= 0) {
    end_op();
    return -1;
  }

  iunlockput(ip);
  end_op();
  return 0;
}
  • kernel/sysfile.c:修改 sys_open(),添加对于软链接文件的处理
uint64
sys_open(void){
  // ...
  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)) {
	    int total = 10;
	    while (ip->type == T_SYMLINK && total > 0) {
	      // 读取软链接文件中存储的 target 路径
	      if(readi(ip, 0, (uint64)path, 0, ip->size) != ip->size) {
	        iunlockput(ip);
	        end_op();
	        return -1;
	      }
	      iunlockput(ip); //释放软链接文件的锁
	      // ip重新赋值为软链接 target 指向的文件
	      if((ip = namei(path)) == 0) {
	        end_op();
	        return -1;
	      }
	      // 锁住,进行下一轮判断
	      ilock(ip);
	      total--;
	    }
	    // 超过循环上限
	    if (total == 0) {
	      iunlockput(ip);
	      end_op();
	      return -1;
	    }
      }
  }
  // ... 其他代码
}

实验结果

  • symlinktest
    在这里插入图片描述
  • 24
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值