ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Lab9 File System

2022-07-15 15:37:16  阅读:179  来源: 互联网

标签:文件 addr ip System bp File Lab9 inode block


Lab9 File System

由于本人这段时间忙于其他事务,这一篇可能会写得比较简略,并且文件系统的有关知识和前面课程的知识不同,文件系统的知识十分庞杂,相应的笔记都会记录在另一篇文章中。

1. Large files(moderate)

1.1 题目

在原始xv6中,每个inode有13个block number(b0,b1,b2……),用来指定文件的数据所在的block,每个block number占4B。其中12个是"direct" block number,另一个是"singly-indirect" block number,这样xv6中一个文件最大可以是(256+12)*1024B=268KB。为了能够表示更大的文件,可以引入"double-indirect" block number。加入有11个直接索引,1个间接索引,1个双重间接索引,这样一个文件最大可以是(11+256+256*256)*1024B=65803KB=64.3MB。

1.2 实现

更改inode中block number的组成

#define NDIRECT 11
#define NINDIRECT (BSIZE / sizeof(uint))
#define NDOUBLE (NINDIRECT*NINDIRECT)
#define MAXFILE (NDIRECT + NINDIRECT + NDOUBLE)

根据提示,更改static uint bmap(struct inode *ip, uint bn),该函数的作用是:对于inode中的第n个block,指出其在磁盘设备中的block num。下面是一个改进之前的inode block number示意图:

static uint
bmap(struct inode *ip, uint bn)
{
    // ...
    if (bn < NDOUBLE) {
        // bmap 第一级目录
        if ((addr = ip->addrs[NDIRECT + 1]) == 0)
          ip->addrs[NDIRECT + 1] = addr = balloc(ip->dev);
        bp = bread(ip->dev, addr);
        a = (uint*)bp->data;
        // bmap 第二级目录
        int num_single_block = bn / NINDIRECT;
        int left = bn % NINDIRECT;
        if ((addr = a[num_single_block]) == 0) {
          a[num_single_block] = addr = balloc(ip->dev);
          log_write(bp);
        }
        brelse(bp);
        bp = bread(ip->dev, addr);
        a = (uint*)bp->data;
        if ((addr = a[left]) == 0) {
          a[left] = addr = balloc(ip->dev);
          log_write(bp);
        }
        brelse(bp);
        return addr;
    }
    // ...
}

注意每次bread某个block后,最后都要回收该block

函数void itrunc(struct inode *ip)的作用是给定一个inode,释放该inode的所有空间,包括它索引数据区block。由于inode的索引结构有所变化,所以在回收时也要做出改变。原本itrunc中释放了一级索引和二级索引,现在需要释放三级索引。

void
itrunc(struct inode *ip)
{
  // ...
  // 释放二级目录
  if (ip->addrs[NDIRECT + 1]) {
    struct buf *out_bp;
    uint *out_a;
    out_bp = bread(ip->dev, ip->addrs[NDIRECT + 1]);
    out_a = (uint*)out_bp->data;
    for (i = 0; i < NINDIRECT; i ++) {
      if (out_a[i]) {
        bp = bread(ip->dev, out_a[i]);
        a = (uint*)bp->data;
        for (j = 0; j < NINDIRECT; j ++) {
          if (a[j])
            bfree(ip->dev, a[j]);
        }
        brelse(bp);
        bfree(ip->dev, out_a[i]);
        // out_a[i] = 0;
      }
    }
    brelse(out_bp);
    bfree(ip->dev, ip->addrs[NDIRECT + 1]);
    ip->addrs[NDIRECT + 1] = 0;
  }
  // ...
}

这里需要注意的是,上述代码中out_a[i] = 0;这句是不需要的,所以被注释掉了,事实上,我们要清除的索引块号仅仅是在inode上的13个索引块号,包括二级索引和三级索引在内的索引块号是不用置0的(虽然置0也没关系),原因是这些用来存block num的块最终是要被回收的,在它们被再次分配时,balloc保证了将它们的所有字节置0.

2.1 题目

有关symbolic link(软链接/符号链接)的知识在xv6手册中似乎并没有介绍。hard link(硬链接)和symlink的知识我是在交大的银杏书《现代操作系统:原理与实现》中学习的,顺便复习了文件系统的有关知识(有一来说一,这本教材来很不错,很适合操作系统入门)。

这里简单用自己的语言讲讲这两个链接,就算是复习吧。

inode是个好东西,可以说是它组织了文件(这里的文件,说的是硬盘上存在的物理文件)。它记录了文件的metadata(元数据),通过inode,我们就可以访问文件中的数据。但是inode同时有个缺点:它是通过inumber为区分的,对于用户来说,这很不方便。于是我们引入了文件名来区分文件,而管理文件名的是目录。目录也是文件,只不过它是file with some datastructure,这里的datastructure是一个称为directory entry(目录项)的东西,一个简单的目录项如下:

可以这么理解,目录数据块存的数据就是一堆目录项,可以通过类似数组的方式访问。我们可以看出,文件名只是一个标识,它是依赖于inode的,信息真正存在于inode中。

Linux中硬链接命令:ln target path

完成的工作是创建一个新的文件path,它使用target的inode,即它俩共用一个inode,毋庸置疑,此操作后这个inode中的nlink=2。如果我们通过更改target中的内容来改变文件数据,那么显然这次更改对path来说是可见的。如果我们删除target,path并不会收到任何影响,只不过inode中的nlink变为了1。如果再将path删除,那么inode中的nlink变为0,操作系统会回收该inode。

Linux中软链接命令:ln -s target path

完成的工作是创建一个新文件path,申请一个新的inode,文件的内容是target这个路径,也就是说该文件存了一个路径。这是一种特殊的文件——符号链接文件。如果尝试打开它,其实是打开了它存的路径中的那个文件,并且这个过程是递归的,如果路径中的文件仍是符号链接文件,继续跟随,直到不是符号链接文件。如果删除target,path就失去了了链接的对象,此时打开它会出错。

符号链接还可以链接目录,这个问题比较复杂,题目没有要求,这里不展开。


题目要求我们实现xv6的符号链接的系统调用。在xv6中,符号链接文件有两种打开方式:跟随或不跟随。所谓不跟随就是直接打开符号链接文件。

2.2 实现

uint64
symlink(char target[], char linkpath[])
{
  struct inode *ip;
  struct buf *bp;
  uint addr;
  ip = create(linkpath, T_SYMLINK, 0, 0); // 申请一个inode用来存新的符号链接文件.create中已经获得了锁
  // ilock(ip);
  if (ip == 0) {
    // iunlockput(ip);
    end_op();
    return -1;
  }
  // ilock(ip);
  if ((addr = ip->addrs[0]) == 0)
    ip->addrs[0] = addr = lazy_balloc(ip->dev);
  bp = bread(ip->dev, addr);
  memmove(bp->data, target, MAXPATH);
  iupdate(ip); // 将这个inode存盘
  iunlockput(ip);
  log_write(bp); // 将这个block存盘
  brelse(bp);

  end_op();
  return 0;
}

这里需要注意create中已经获得了锁,所以不能在外面再获得锁。还有最后解锁的时候要用iunlockput(ip),它在解锁的同时使内存中inode的引用计数减1。

上面的实现方法是将路径名写在b0索引的那个block中,似乎也可以直接无视inode的数据结构,直接写在inode上,每个inode有64字节的空间,表示一个路径名足够了。

修改sys_open系统调用来支持打开符号链接文件

uint64
sys_open(void)
{
  // ...
  int count = 0;
  while (ip->type == T_SYMLINK) {
    count ++;
    if (count > 10) {
      iunlockput(ip);
      end_op();
      return -1;
    }
    // printf("dead loop\n");
    char linkpath[MAXPATH];
    struct inode *tmp_ip;
    struct buf *bp;
    if (ip->addrs[0] == 0) {
      iunlockput(ip);
      end_op();
      return -1;
    }
    bp = bread(ip->dev, ip->addrs[0]);
    memmove(linkpath, bp->data, MAXPATH);
    brelse(bp);

    if ((tmp_ip = namei(linkpath)) == 0) {
      iunlockput(ip);
      end_op();
      return -1;
    }
    if (omode & O_NOFOLLOW) {
      break;
    } else {
      iunlockput(ip);
      ip = tmp_ip;
      ilock(ip);
    }
  }
  // ...
}

标签:文件,addr,ip,System,bp,File,Lab9,inode,block
来源: https://www.cnblogs.com/123chen-jiahui/p/16481569.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有