• MIT-6.S081-2020实验(xv6-riscv64)九:fs


    实验文档

    概述

    这次实验涉及文件系统,重点是对inode节点的操作。

    内容

    Large files

    这个任务主要目的是支持更大的文件。和内存映射类似,文件系统中也有一个类似“页表”的结构,每个文件(inode)都有自己的一个“页表”,维护自己文件占用的文件块。和内存不同的是,这个“页表”的级别是自定义的,原始的xv6的表有13项,前12项直接包含文件块的地址,第13项是二级表的地址,二级表包含256项,每项都是文件块的地址,所以单个文件最大为12+256个文件块,为了支持更大的文件,需要将直接包含文件块地址的表项中取一项支持三级表,这样单个文件就可以扩大到11+256+256*256块。整体思路还是很清晰的,bmap函数添加:

      bn -= NINDIRECT;
    
      if(bn < NININDIRECT){
          int lev1 = bn / NINDIRECT, lev2 = 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[lev1]) == 0){
          a[lev1] = addr = balloc(ip->dev);
          log_write(bp);
        }
        brelse(bp);
        bp = bread(ip->dev, addr);
        a = (uint*)bp->data;
        if((addr = a[lev2]) == 0){
          a[lev2] = addr = balloc(ip->dev);
          log_write(bp);
        }
        brelse(bp);
        return addr;
      }
      panic("bmap: out of range");
    

    itrunc函数也做相应的添加:

      if(ip->addrs[NDIRECT + 1]){
        bp = bread(ip->dev, ip->addrs[NDIRECT + 1]);
        a = (uint*)bp->data;
        for(j = 0; j < NINDIRECT; j++) {
          if(a[j]) {
            bp2 = bread(ip->dev, a[j]);
            a2 = (uint*)bp2->data;
            for(k = 0; k < NINDIRECT; k++) if (a2[k]) bfree(ip->dev, a2[k]);
            brelse(bp2);
            bfree(ip->dev, a[j]);
          }
        }
        brelse(bp);
        bfree(ip->dev, ip->addrs[NDIRECT + 1]);
        ip->addrs[NDIRECT + 1] = 0;
    

    这个任务主要实现软符号链接,个人还是觉得有点难度,需要对inode节点的各类操作比较熟悉。首先是给inode和dinode添加一个字符串属性用来存储符号链接中的目标路径,比如dinode:

    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
      char target[MAXTARGET];
    };
    

    主要这个MAXTARGET这个常量是有讲究的,因为dinode是一个一个排布在一个硬盘块里面的,所以硬盘块的大小必须是dinode结构体大小的倍数,硬盘块的大小是常量BSIZE的大小,即1024字节,当没有target属性时,dinode的大小为2*4+4+4*13=64,刚好被1024整除,同时查看param.h可以发现xv6规定的路径长度最大可以为128,所以MAXTARGET还得大于128,因此我取MAXTARGET=192,192+64=256,被1024整除。

    然后就是sys_symlink:

    uint64 sys_symlink(void) {                                                                                       char target[MAXPATH], path[MAXPATH];
        if(argstr(0, target, MAXPATH) < 0 || argstr(1, path, MAXPATH) < 0)
            return -1;
        begin_op();
        struct inode *ip = create(path, T_SYMLINK, 0, 0);
        if (ip == 0) {
            end_op(); return -1;
        }
        memmove(ip->target, target, sizeof(target));
        iupdate(ip); iunlockput(ip);
        end_op(); return 0;
    }
    

    首先需要注意获得传入系统调用的字符串需要用argstr,不能用argaddr获得地址后自己复制,因为那个地址是用户内存的虚拟地址,现在在内核态,页表已经被换掉了。这里调用了create函数来创建符号文件,因为create函数里没有对inode的target属性赋值,所以需要在这里处理。另外就是create函数返回的有效inode是已经经过iget和ilock的了,所以这里create完就直接赋值target属性并更新到对应的dinode,然后iunlock解锁再iput释放inode指针(合起来是iunlockput函数)。

    接着是对open函数的修改,主要就是添加一个沿着符号链接不断查找的过程,经过查找后得到的路径才是需要open的真正路径:

      if(omode & O_CREATE){
          ......
      } else {
          int cnt = 0;
          for (;;) {
              ip = namei(path);
              if (ip == 0) {
                  end_op(); return -1;
              }
              ilock(ip);
              if(ip->type != T_SYMLINK || (omode & O_NOFOLLOW)) break;
              memmove(path, ip->target, MAXPATH);
              iunlockput(ip);
              cnt++;
              if (cnt > 9) {
                  end_op(); return -1;
              }
          }
          ......
    

    namei得到的inode是经过iget但没ilock的,所以取属性和修改属性需先ilock。另外就是memmove参数的最后一个参数需要是MAXPATH而不是sizeof(ip->target),因为inode的target属性我跟随dinode的target属性都设成192字节的字符串,大小大于MAXPATH即128,所以如果按sizeof来复制会溢出。


    由于我使用的是虚拟机,虽然我已经通过使用无桌面版arch linux+ssh控制来尽可能减少CPU和内存的消耗了,但文件读写还是非常难顶。虽然程序单独测试正确,思路也和网上别人通过的程序思路基本一样,最后make grade的时候还是超时了,不得已,把测试脚本里的bigfile和usertests测试时限都改成10分钟才通过(usertests里的writebig好像在这次实验里格外耗时)。估计使用真机或WSL读写真磁盘来完成本实验情况会好一些。

  • 相关阅读:
    给大家来一波免费电影福利~~~
    SpringBoot第二十四篇:应用监控之Admin
    影响世界的100个经典管理定律
    SpringBoot第二十三篇:安全性之Spring Security
    20年研发管理经验谈(十六)(终结)
    Java获取指定时间段的年份(开始、结束时间)、月份(开始、结束时间)、天数(开始、结束时间)
    js创建post请求
    Java截取视频首帧并旋转正向
    jquery 禁用/启用滚动条
    Jquery表单序列化和json操作
  • 原文地址:https://www.cnblogs.com/YuanZiming/p/14264674.html
Copyright © 2020-2023  润新知