前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MIT_6.s081_Lab9:Xv6 and File System

MIT_6.s081_Lab9:Xv6 and File System

作者头像
用户7267083
发布2022-12-08 14:49:52
3530
发布2022-12-08 14:49:52
举报
文章被收录于专栏:sukuna的博客sukuna的博客

MIT_6.s081_Lab9:Xv6 and File System

于2022年3月8日2022年3月8日由Sukuna发布

Lab 9_1 Large files

在这个实验中,你会拓展文件系统中文件的最大大小,其中一开始文件是12个直接连接块,1个一级索引块,一共16*16+12=268个块,这个时候我们要修改一下改成11个直接相连,1个一级索引块,1个二级索引块,一共256*256+256+11=65536+256+11个块.

其中xv6把文件系统映射到fs.img中,这个一共有二十万个块,其中70个保存块信息的meta块,剩下的都是数据.完成这个实验你需要关注磁盘 inode 的格式,这个由 fs.h 中的 struct dinode 定义。 您对 NDIRECT、NINDIRECT、MAXFILE 和结构 dinode 的 addrs[] 元素特别感兴趣。 查看 xv6 文本中的图 8.3 以了解标准 xv6 inode 的图表。 在磁盘上查找文件数据的代码在 fs.c 的 bmap() 中。 看看它,确保你明白它在做什么。 bmap() 在读取和写入文件时都会被调用。 写入时,bmap() 会根据需要分配新块来保存文件内容,并在需要时分配间接块来保存块地址。 bmap() 处理两种块号。 bn 参数是一个“逻辑块号”——文件中的块号,相对于文件的开头。 ip->addrs[] 中的块号和 bread() 的参数是磁盘块号。 您可以将 bmap() 视为将文件的逻辑块号映射到磁盘块号。

修改 bmap() ,使其除了直接块和单间接块外,还实现双重间接块。 你只需要 11 个直接块,而不是 12 个,就可以为新的双重间接块腾出空间; 您不能更改磁盘 inode 的大小。 ip->addrs[] 的前 11 个元素应该是直接块; 第 12 个应该是一个单独的间接块(就像现在的块一样); 第 13 个应该是你新的双重间接块。

1) 修改直接映射的数量.
代码语言:javascript
复制
#define NDIRECT 11#define NDIRECT 11
uint addrs[NDIRECT+2];
#define MAXFILE (NDIRECT + NINDIRECT + NDOUBLE)//文件最大值也要改变

其中直接映射的数量由原来的12个改为11个.

2) 对应的file.h的inode结构体也要进行更改
代码语言:javascript
复制
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];
};
3) 阅读bmap的代码: 0-NDIRECT-1 :直接 ,NDIRECT,NDIRECT+NINDIRECT-1:一级间接
代码语言:javascript
复制
//bn:the number the blocks.
  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;
  }

有个大前提,就是如果索引为0就代表这个磁盘为空,需要申请一个返回一个磁盘块号

首先第一部分,就是直接映射的部分,如果寻找的逻辑地址是在NDIRECT以内的,就可以直接访问磁盘块.

第二部分就是一级间接映射,首先先把一级映射的映射表找到,读出来,如果没有映射表就新建一个.映射表我们之前在操作系统学过就是一个int类型数组,这个时候我们就可以把映射表解释称int类型数组,然后根据索引值,也就是bn-NDIRECT来寻找,寻找就是寻找数组里面的东西.

4) 依葫芦画瓢作出二级索引.
代码语言:javascript
复制
  bn -= NINDIRECT;

  if(bn < NDOUBLE){
    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 bn1 = bn / NINDIRECT,bn2 = bn % NINDIRECT;
    //first suoyin
    if((addr = a[bn1]) == 0){
     a[bn1] = addr = balloc(ip->dev);
     log_write(bp);
    }
    //second suoyin
    brelse(bp);
    bp=bread(ip->dev,addr);
    a=(uint*)bp->data;
    if((addr=a[bn2])==0)
    {
        a[bn2]=addr=balloc(ip->dev);
        log_write(bp);
    }
    brelse(bp);
    return addr;
  }

二级索引需要两个索引值,一个是除得来的结果,一个是%得来的结果,首先先读一次,获得一级索引表,做法和上面是一样的,接着再读,根据一级索引表得来的结果就是对应二级索引表的位置,所以说根据一级索引表的结果打开二级索引表,找到最后属于我们的索引值.

5) 完成索引的释放.
代码语言:javascript
复制
  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(i = 0; i < NINDIRECT; i++) {
          if(a2[i]) bfree(ip->dev, a2[i]);
        }
        brelse(bp2);
        bfree(ip->dev, a[j]);
        a[j] = 0;
      }
    }
    brelse(bp);
    bfree(ip->dev, ip->addrs[NDIRECT+1]);
    ip->addrs[NDIRECT] = 0;
  }

就是先读取数组的第13项,构造一个二重循环,先进入到第一个一级索引的第一个二级索引陆续地释放所有的块.再进入到第一个一级索引的第二个二级索引…第一个一级索引的第256个二级索引….第256个二级索引的第256个一级索引…

Lab9_2 Symbolic links

您将实现 symlink(char *target, char *path) 系统调用,它会在 path 处创建一个新的符号链接,该链接引用由 target 命名的文件。

首先,为symlink创建一个新的系统调用号,在user/usys.pl,user/user.h中添加一个入口,在kernel/sysfile.c中实现一个空的sys_symlink。 将新文件类型 (T_SYMLINK) 添加到 kernel/stat.h 以表示符号链接。 向 kernel/fcntl.h 添加一个新标志 (O_NOFOLLOW),可与 open 系统调用一起使用。请注意,传递给 open 的标志是使用按位 OR 运算符组合的,因此您的新标志不应与任何现有标志重叠。这将让您在将 user/symlinktest.c 添加到 Makefile 后编译它。 实现 symlink(target, path) 系统调用以在指向目标的路径上创建一个新的符号链接。请注意,系统调用成功时不需要存在目标。您将需要选择某个位置来存储符号链接的目标路径,例如,在 inode 的数据块中。 symlink 应该返回一个表示成功 (0) 或失败 (-1) 的整数,类似于链接和取消链接。 修改 open 系统调用以处理路径引用符号链接的情况。如果文件不存在,则打开必须失败。当进程在要打开的标志中指定 O_NOFOLLOW 时, open 应该打开符号链接(而不是跟随符号链接)。 如果链接文件也是符号链接,则必须递归地跟随它,直到到达非链接文件。如果链接形成循环,则必须返回错误代码。如果链接的深度达到某个阈值(例如,10),您可以通过返回错误代码来近似此值。 其他系统调用(例如,链接和取消链接)不得遵循符号链接;这些系统调用对符号链接本身进行操作。 对于本实验,您不必处理指向目录的符号链接。

1) 添加系统调用.(略)
2) 按照实验提示的信息,添加两个新的宏.(略)
3) 完成symlink系统调用.

首先第一点,我们发现,在sysfile.c的系统调用是需要提交事务的,我们也模仿这一点进行更改,处理的思路就是获取参数,创建一个新的inode,把符号连接的文件路径写进inode里面,然后提交即可.

代码语言:javascript
复制
uint64
sys_symlink(void)
{  
  char path[MAXPATH], target[MAXPATH];
  struct inode *ip;
  // get parameter
  if(argstr(0, target, MAXPATH) < 0)
    return -1;
  if(argstr(1, path, MAXPATH) < 0)
    return -1;
  begin_op();
  // create a inode
  if((ip = create(path, T_SYMLINK, 0, 0)) == 0) {
    end_op();
    return -1;
  }
  // write the path of the file to the inode.
  if(writei(ip, 0, (uint64)target, 0, MAXPATH) < MAXPATH) {
    iunlockput(ip);
    end_op();
    return -1;
  }
  iunlockput(ip);
  end_op();
  return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年3月8日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • MIT_6.s081_Lab9:Xv6 and File System
    • Lab 9_1 Large files
      • Lab9_2 Symbolic links
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档