笔记 Lab9: File System | 文件系统

Large files (moderate)

Modify bmap() so that it implements a doubly-indirect block, in
addition to direct blocks and a singly-indirect block. You’ll have to
have only 11 direct blocks, rather than 12, to make room for your new
doubly-indirect block; you’re not allowed to change the size of an
on-disk inode. The first 11 elements of ip->addrs[] should be direct
blocks; the 12th should be a singly-indirect block (just like the
current one); the 13th should be your new doubly-indirect block. You
are done with this exercise when bigfile writes 65803 blocks and
usertests runs successfully:

第一个实验是让我们通过改变inode的映射机制, 使得一个文件, 即一个struct inode, 可以占据更大的大小. 我们需要将单层映射改写为双层映射. 每个文件由原来最多占据 12+ 256个data blocks改写为最多占据 11 + 256 + 256* 256个data blocks, 大大的增加了文件可用大小.
笔记 Lab9: File System | 文件系统_第1张图片

// kernel/fs.c

// note: NDIRECT=12
// 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
};
// kernel/file.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]; // NDIRECT+1 -> NDIRECT+2
};

修改 bmap(获取 inode 中第 bn 个块的块号)和 itrunc(释放该 inode 所使用的所有数据块),让其能够识别二级索引。(基本上和复制粘贴一致,只是在查出一级块号后,需将一级块中的数据读入,然后再次查询)

// kernel/fs.c

// Return the disk block address of the nth block in inode ip.
// If there is no such block, bmap allocates one.
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){ // singly-indirect
    // 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 < NINDIRECT * NINDIRECT) { // doubly-indirect
    // 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[bn/NINDIRECT]) == 0){
      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;
  }

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

// Truncate inode (discard contents).
// Caller must hold ip->lock.
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(j = 0; j < NINDIRECT; j++){
      if(a[j]) {
        struct buf *bp2 = bread(ip->dev, a[j]);
        uint *a2 = (uint*)bp2->data;
        for(int 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;
  }

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

运行结果

$ bigfile
..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
wrote 65803 blocks
bigfile done; ok

Symbolic links (moderate)

这个实验是要实现类似linux上的symlink软链接功能, 比如symlink(‘a’, ‘b’)后, open(‘b’)这个指令会实际上索引到文件’a’所对应的文件data block.

首先我们按常规操作, 把一个新的系统调用sys_symlink的代号, 跳转函数等等设立好. 然后我们直接开始看sys_symlink的实现: 我们把其对应的inode的type设为T_SYMLINK, 然后在其data block的[0, MAXPATH]的范围里写上所要链接的target path的路径.

# kernel/sysfile.c

int sys_symlink(char *target, char *path) {
  char kpath[MAXPATH], ktarget[MAXPATH];
  memset(kpath, 0, MAXPATH);
  memset(ktarget, 0, MAXPATH);
  struct inode *ip;
  int n, r;

  if((n = argstr(0, ktarget, MAXPATH)) < 0)
    return -1;

  if ((n = argstr(1, kpath, MAXPATH)) < 0)
    return -1;

  int ret = 0;
  begin_op();

  // 这个软链接已经存在了
  if((ip = namei(kpath)) != 0){
    // symlink already exists
    ret = -1;
    goto final;
  }

  // 为这个软链接allocate一个新的inode
  ip = create(kpath, T_SYMLINK, 0, 0);
  if(ip == 0){
    ret = -1;
    goto final;
  }
  // 把target path写入这个软链接inode的数据[0, MAXPATH]位置内
  if ((r = writei(ip, 0, (uint64)ktarget, 0, MAXPATH)) < 0)
    ret = -1;
  iunlockput(ip);

final:
  end_op();
  return ret;
}

然后在sys_open这个函数里, 如果传入的path是一个软链接的话, 我们需要为用户递归去"寻址", 直到找到第一个不是软链接的path (或者递归层数太高, 默认陷入了死循环, 直接返回-1)

# kernel/sysfile.c

uint64
sys_open(void)
{
  ... 省略, 和之前源码一样 ...

  if(omode & O_CREATE){
    ... 省略, 和之前源码一样 ...
  }

  // 是软链接且O_NOFOLLOW没被设立起来
  int depth = 0;
  while (ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)) {
    char ktarget[MAXPATH];
    memset(ktarget, 0, MAXPATH);
    // 从软链接的inode的[0, MAXPATH]读出它所对应的target path
    if ((r = readi(ip, 0, (uint64)ktarget, 0, MAXPATH)) < 0) {
      iunlockput(ip);
      end_op();
      return -1;
    }
    iunlockput(ip);
    if((ip = namei(ktarget)) == 0){ // target path 不存在
      end_op();
      return -1;
    }

    ilock(ip);
    depth++;
    if (depth > 10) {
      // maybe form a cycle 默认死循环
      iunlockput(ip);
      end_op();
      return -1;
    }
  }

  ... 省略, 和之前源码一样 ...
}

把symlinktest加入Makefile后进行编译, 顺利通过测试.

你可能感兴趣的:(mit6.s081,笔记)