首页 > 其他分享 >Mit6.S081笔记Lab9: file system 文件系统

Mit6.S081笔记Lab9: file system 文件系统

时间:2024-11-15 15:42:52浏览次数:1  
标签:addr Mit6 ip bn system NDIRECT dev file inode

课程地址:https://pdos.csail.mit.edu/6.S081/2020/schedule.html
Lab 地址:https://pdos.csail.mit.edu/6.S081/2020/labs/fs.html
我的代码地址:https://github.com/Amroning/MIT6.S081/tree/fs
xv6手册:https://pdos.csail.mit.edu/6.S081/2020/xv6/book-riscv-rev1.pdf
相关翻译:https://xv6.dgs.zone/labs/requirements/lab9.html
参考博客:https://blog.miigon.net/posts/s081-lab9-file-system/

学习笔记记录,如有错误恳请各位大佬指正

Lab9: file system

​ 增加XV6系统文件的最大大小(当前只支持268kb以下的文件);实现软链接

完整题目要求请去顶部链接查看

Large files(moderate)

在本作业中,您将增加xv6文件的最大大小。目前,xv6文件限制为268个块或268*BSIZE字节(在xv6中BSIZE为1024)。此限制来自以下事实:一个xv6 inode包含12个“直接”块号和一个“间接”块号,“一级间接”块指一个最多可容纳256个块号的块,总共12+256=268个块。

您将更改xv6文件系统代码,以支持每个inode中可包含256个一级间接块地址的“二级间接”块,每个一级间接块最多可以包含256个数据块地址。结果将是一个文件将能够包含多达65803个块,或256*256+256+11个块(11而不是12,因为我们将为二级间接块牺牲一个直接块号)

​ 原本的XV6文件系统中,inode的结构如下:

// kernel/fs.h
#define NDIRECT 12
#define NINDIRECT (BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + 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+1];   // Data block addresses
};

addrs字段用来索引记录数据的所在盘块号。每个文件所占用的前 12 个盘块的盘块号是直接记录在 inode 中的(每个盘块 1024 字节),所以对于任何文件的前 12 KB 数据,都可以通过访问 inode 直接得到盘块号。这一部分称为直接记录盘块(addr[0~11],12个直接块)

​ 对于大于 12 个盘块的文件,大于 12 个盘块的部分,会分配一个额外的一级索引表(addr[11],1024Byte),用于存储这部分数据的所在盘块号。一个索引项是4字节,一级索引表可以包含 BSIZE(1024) / 4 = 256 个盘块号,加上 inode 中的 12 个盘块号,一个文件最多可以使用 12+256 = 268 个盘块,也就是 268KB

​ 该实验需要修改inode的结构,修改成11个直接块,1个一级间接块,1个二级间接块。所以可以先修改inode的结构体,注意这是磁盘inode的结构体:

// kernel/fs.h
#define NDIRECT 11                                               // 直接块数量
#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];   // 0~10:直接索引    11:一级间接索引    12:二级间接索引
};

​ 然后还需要修改内存中inode的副本结构体:

// kernel/fs.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];
};

​ 直接块腾一个位置出来给二级间接块

​ 需要修改的第一个函数是bmap,这个函数是将inode中的第bn个逻辑块号和物理块号简历映射,其原理备注在源代码中(这里是解释原本的bmap):

// kernel/fs.c
static uint
bmap(struct inode *ip, uint bn)
{
  uint addr, *a;
  struct buf *bp;

  // 如果bn小于直接块数量,则按直接映射处理
  if(bn < NDIRECT){
    if((addr = ip->addrs[bn]) == 0)				// 如果对应的物理块号为0,表示还没分配,则分配一个新的物理块,建立映射
      ip->addrs[bn] = addr = balloc(ip->dev);
    return addr;
  }
    
  // 到这里已经是间接块了,减去直接块的数量得到间接块的逻辑块号
  bn -= NDIRECT;

  if(bn < NINDIRECT){
    // 如果间接块还没分配,分配一个
    if((addr = ip->addrs[NDIRECT]) == 0)			// 0~NDIRECT-1是直接块,NDIRECT是间接块
      ip->addrs[NDIRECT] = addr = balloc(ip->dev);	// 此时这个块可以映射256个块
      
    // 获取刚分配的缓存块,检查bn对应的块,如果为0则没有分配,建立映射
    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);			// 建立映射结束,释放掉缓存块(可以通过ip->dev和bn找到这个块了)
    return addr;
  }

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

​ 清楚了原理,加上刚才增加的二级映射,就很好修改了:

// kernel/fs.c
static uint
bmap(struct inode *ip, uint bn)
{
  uint addr, *a;
  struct buf *bp;

  // 如果bn小于直接块数量,则按直接映射处理
  if(bn < NDIRECT){
    if((addr = ip->addrs[bn]) == 0)				// 如果对应的物理块号为0,表示还没分配,则分配一个新的物理块,建立映射
      ip->addrs[bn] = addr = balloc(ip->dev);
    return addr;
  }
    
  // 到这里已经是一级间接块了,减去直接块的数量得到间接块的逻辑块号
  bn -= NDIRECT;

  if(bn < NINDIRECT){
    // 如果间接块还没分配,分配一个
    if((addr = ip->addrs[NDIRECT]) == 0)			// 0~NDIRECT-1是直接块,NDIRECT是间接块
      ip->addrs[NDIRECT] = addr = balloc(ip->dev);	// 此时这个块可以映射256个块
      
    // 获取刚分配的缓存块,检查bn对应的块,如果为0则没有分配,建立映射
    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);			// 建立映射结束,释放掉缓存块(可以通过ip->dev和bn找到这个块了)
    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 / NINDIRECT]) == 0) {        // bn处在二级索引中间级的第 bn/NINDIRECT 个索引处
          a[bn / NINDIRECT] = addr = balloc(ip->dev);
          log_write(bp);
      }
      brelse(bp);

      // 最后一级索引
      bn %= NINDIRECT;
      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;
  }

​ 还有一个函数itrunc,释放该inode所映射的所有数据块。原理类似,直接循环释放所有数据块:

// kernel/fs.c
void
itrunc(struct inode *ip)
{
  int i, j;
  struct buf *bp;
  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]) {
              struct buf* bp2 = bread(ip->dev, a[i]);
              uint* a2 = (uint*)bp2->data;
              for (int 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->addrs[NDIRECT + 1] = 0;
  }

  ip->size = 0;
  iupdate(ip);
}

此时可以验证实验是否通过

Symbolic links(moderate)

您将实现symlink(char *target, char *path)系统调用,该调用在引用由target命名的文件的路径处创建一个新的符号链接。有关更多信息,请参阅symlink手册页(注:执行man symlink)。要进行测试,请将symlinktest添加到*Makefile*并运行它。当测试产生以下输出(包括usertests运行成功)时,您就完成本作业了。

​ 该实验需要实现软链接机制,实现一个软链接系统调用函数symlink

​ 我习惯先把新的系统调用函数声明给做好,详细添加步骤参考Lab2笔记,涉及文件:syscall.hsyscall.cusys.pluser.h

​ 根据提示,添加新的文件类型T_SYMLINK,表示文件为软链接文件:

// kernel/stat.h
#define T_DIR     1   // Directory
#define T_FILE    2   // File
#define T_DEVICE  3   // Device
#define T_SYMLINK 4   // 软链接

​ 添加新的文件标志位O_NOFOLLOW,该标志可用于open系统调用:

// kernel/fcntl.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 

​ 实现具体的sys_symlink,使用create创建一个加了锁的指向源文件的inode,再将链接目标文件地址通过writei写入inode,操作完之后使用iunlockput解锁创建的inode并使其引用计数-1,表示创建软链接的操作结束,释放锁:

// kernel/sysfile.c
// 软链接
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();

    ip = create(path, T_SYMLINK, 0, 0);     // 创建一个新的inode,类型为T_SYMLINK,指向path文件
    if (ip == 0) {
        end_op();
        return -1;
    }

    if (writei(ip, 0, (uint64)target, 0, strlen(target)) < 0) {     // 将target路径写入inode
        end_op();
        return -1;
    }

    iunlockput(ip);
    end_op();

    return 0;
}

​ 修改sys_open函数,如果通过循环调用namei 获取对应的inode,如果指向的仍是软链接,就继续循环读取,直到找到真正指向的文件,或者超过了一定的链接深度:

// kernel/sysfile.c
uint64
sys_open(void)
{
  ......

  if(omode & O_CREATE){
    ip = create(path, T_FILE, 0, 0);
    if(ip == 0){
      end_op();
      return -1;
    }
  }
  else {
      int symlink_depth = 0;
      while (1) {
          if ((ip = namei(path)) == 0) {    // 解析路径,获取对应的inode
              end_op();
              return -1;
          }

          ilock(ip);
          if (ip->type == T_SYMLINK && (omode & O_NOFOLLOW) == 0) {     //如果当前指向的仍是软链接,则继续循环
              if (++symlink_depth > 10) {               // 链接深度超过10层就退出
                  iunlockput(ip);
                  end_op();
                  return -1;
              }
              if (readi(ip, 0, (uint64)path, 0, MAXPATH) < 0) {     // 读取链接的目标路径
                  iunlockput(ip);
                  end_op();
                  return -1;
              }
              iunlockput(ip);
          }
          else
              break;
      }

      if (ip->type == T_DIR && omode != O_RDONLY){
          iunlockput(ip);
          end_op();
          return -1;
    }
  }

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

  ......
}

​ 此时可以验证实验是否通过

标签:addr,Mit6,ip,bn,system,NDIRECT,dev,file,inode
From: https://www.cnblogs.com/Amroning/p/18548087

相关文章

  • [转]Windows下编写的shell脚本,传到Linux执行出现bad interpreter:No such file or di
    原文地址:badinterpreter:Nosuchfileordirectory解决方法-jprothwell-博客园在Linux中执行.sh脚本,异常/bin/sh^M:badinterpreter:No such file or directory。分析:这是不同系统编码格式引起的:在windows系统中编辑的.sh文件可能有不可见字符,所以在Linux系统下执......
  • dockerfile中cmd和entrypoint的区别
    cmd语法TheCMDinstructionhasthreeforms:CMD["executable","param1","param2"](execform,thisisthepreferredform)CMD["param1","param2"](asdefaultparameterstoENTRYPOINT)CMDcommandparam1par......
  • Unable to load io.netty.resolver.dns.macos.MacOSDnsServerAddressStreamProvider,
     macm1启动项目,报错,“Unabletoloadio.netty.resolver.dns.macos.MacOSDnsServerAddressStreamProvider,fallbacktosystemdefaults.ThismayresultinincorrectDNSresolutionsonMacOS.”,出现这个问题是因为使用了spring-cloud-starter-gateway依赖,这需要额外安装......
  • C# read json file throw exception: Could not load file or assembly 'System.Runti
    Couldnotloadfileorassembly'System.Runtime.CompilerServices.Unsafe,Version=4.0.4.1,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a'oroneofitsdependencies.Thelocatedassembly'smanifestdefinitiondoesnotmatchtheassemblyr......
  • 添加一个makefile,该makefile用来构建我的脚手架
    用来构建https://github.com/AgoCan/code-generator#Makefile#变量定义BINARY_NAME=myappGO=goGOFLAGS=#默认构建目标all:windowsx86darwinaarch64#Windowsx86构建windows: @echo"BuildingforWindowsx86..." GOOS=windowsGOARCH=amd64$(GO)build......
  • linux c --- 创建子进程 system() 与 exec() 的区别
    功能差异:system()函数的主要功能是执行一个系统命令,这个命令由用户通过命令行参数传递给system()函数。system()函数会创建一个新的进程来执行这个命令,并等待这个进程执行完毕后才返回。因此,system()函数主要用于执行简单的、可以通过命令行传递参数的系统命令。exec()函数则是......
  • 科普文:软件架构数据库系列之【MySQL状态参数:open table浅析和[ERROR] Error in accept
    概叙科普文:软件架构数据库系列之【MySQL状态参数:tablecache源码浅析】-CSDN博客MySQL经常会遇到Toomanyopenfiles,MySQL上的open_files_limit和OS层面上设置的openfilelimit有什么关系?源码中也会看到不同的数据结构,TABLE,TABLE_SHARE,跟表是什么关系?MySQLflushtable......
  • c# 变量引发了类型“System.NullReferenceException”的异常【未解】
    2024/11/14求助,为什么“targetRoundId1”引发了类型“System.NullReferenceException”的异常代码:Dictionary<string,Dictionary<string,string>>exchangeRateDict1=new();exchangeRateDict1.Add("USD",newDictionary<string,string>{{"USD&q......
  • PROG2004 A simple appointment system
     AssessmentBriefPROG2004OBJECTORIENTEDPROGRAMMINGSummaryTitleAssessment1–Programmingtasks-asimpleappointmentsystemforahealthservice TypeProgrammingDueDateMonday11November202411:59pmAEST(StartofWeek3)LengthNAWeighting20%......
  • CSC3150 memory-mapped files
    CSC3150-Instruction-A3:IntroductionThisassignmentusesxv6,asimpleandUnix-liketeachingoperatingsystem,astheplatformtoguideyouinimplementingthemmapandmunmpsystemcalls.Thesetwoareusedtosharememoryamongprocesesandtomapfil......