Lab9 File system


Lab链接:Lab: file system

Lab源码:momo/MIT-6S081/fs - Gitee.com

1 Large files

题目

当前xv6文件最大只能存储268 blocks的数据。你的任务是修改xv6的代码,将inode结点中的一个“直接索引”改为“二级索引”:该索引指向的block包含256个间接block的地址,每个间接block又包含256个数据block的地址,以扩大文件可存储数据的容量。

思路

struct inode中的NDIRECT直接索引的盘块号减一,腾出一个索引用于二级索引,其他变量随之改动。此时 struct dinode和struct inode的addrs数组大小应为[NDIRECT+2]

// kernel/fs.h

#define NDIRECT 11 // 12->11
#define NINDIRECT (BSIZE / sizeof(uint))
#define NDOUBLEINDIRECT (NINDIRECT*NINDIRECT)
#define MAXFILE (NDIRECT + NINDIRECT + NDOUBLEINDIRECT)

struct dinode {
  // ...
  uint addrs[NDIRECT+2];   // [NDIRECT+1] -> [NDIRECT+2]
};

struct inode {
  // ...
  uint addrs[NDIRECT+2];   // [NDIRECT+1] -> [NDIRECT+2]
};

重点是对bmap()itruc()的修改,两者的逻辑几乎一致。

bn表示当前需要读取的是inode的第几个block,是从0开始的,所以:

  • bn < NDIRECT,当前block地址应由直接索引得到
  • bn-NDIRECT < NDOUBLEINDIRECT,当前block地址由一级索引得到
  • 若都不满足,当前block地址由二级索引得到

增加的是对二级索引的操作,首先通过bn/NINDIRECT得到一级block的地址,再通过bn%NINDIRECT,从一级block中得到二级block的地址,也就是目标数据块的地址。

// fs.c bmap(struct inode *ip, uint bn)

bn -= NINDIRECT;

if (bn < NDOUBLEINDIRECT) {
// inode
if((addr = ip->addrs[NDIRECT+1]) == 0)
  ip->addrs[NDIRECT+1] = addr = balloc(ip->dev);
bp = bread(ip->dev, addr);
a = (uint*)bp->data;

// block 1
if((addr = a[bn/NINDIRECT]) == 0) {
  a[bn/NINDIRECT] = addr = balloc(ip->dev);
  log_write(bp);
}
brelse(bp);
bp = bread(ip->dev, addr);
a = (uint*)bp->data;

// block 2
if((addr = a[bn%NINDIRECT]) == 0) {
  a[bn%NINDIRECT] = addr = balloc(ip->dev);
  log_write(bp);
}
brelse(bp);

题目

实现xv6的符号链接功能:symlink(char *target, char *path),要求path路径代表的文件能链接到target代表的文件。

思路

硬链接和软链接的本质区别:

  • 硬链接是不同的路径指向同一个inode文件
  • 符号链接是两个不同inode的文件,目标文件的路径以字符串格式保存在符号链接的数据区域中,通过读取符号链接数据区域的内容得到目标文件。

本任务包含两个部分:①创建软链接,②根据符号链接打开目标文件

① 创建软链接

这其实是操作系统提供给用户程序的一种服务,所以首先要实现系统调用。

重点是sys_symlink(char *target, char *path)函数的实现。

首先调用create()path中创建一个文件项,得到该文件项的inode指针ip。创建文件不仅需要申请inode并写入相关元数据,还需要找到该文件所在目录,并将该inode作为文件项加入目录,create()已经帮我们完成这些,但调用返回时仍持有该inode的锁,要在创建符号链接之后释放这个锁。

其次,调用writei()向该inode写入目标文件的路径字符串。

// sysfile.c

uint64
sys_symlink(void) 
{
  char target[MAXPATH], path[MAXPATH];
  struct inode *ip;

  if(argstr(0,target,MAXPATH) < 0 || argstr(1,path,MAXPATH) < 0) 
    return -1;

  begin_op();

  if((ip = create(path, T_SYMLINK, 0, 0)) == 0) {
    end_op();
    return -1;
  }

  if(writei(ip, 0, (uint64)target, 0, sizeof(target)) != sizeof(target)) {
    iunlockput(ip);
    end_op();
    return -1;
  }
  iunlockput(ip);
  end_op();
  return 0;
}

② 根据符号链接打开目标文件

open()已经通过namei(char *path)得到路径指向文件的inode。

若open模式为O_NOFOLLOW,**则无需调用follow_symlink()**,inode应当看作F_FILE类型的文件,进行读写操作。因此,调用follow_symlink()需要满足以下条件:

// sysfile.c open()

if(ip->type == T_SYMLINK && omode != O_NOFOLLOW) {
    ip = follow_symlink(ip, 0);
    if(ip == 0) {
      end_op();
      return -1;
    }
}

follow_symlink()返回为0,表示读取失败,提前结束。

follow_symlink()接受两个参数:当前需要读取文件的inode指针,当前是第几次递归调用,其执行逻辑如下:

  1. 通过读取ip指向的inode数据区中的内容得到目标文件的路径
  2. 通过namei()得到目标文件的inode
  3. 若当前inode类型为F_FILE,则得到了真正目标文件的inode,返回;否则当前inode也是符号链接,将自身的inode指针传入,继续递归

凡是对inode中type字段进行操作的指令,都需要获取该inode的锁才能执行。

// sysfile.c

struct inode*
follow_symlink(struct inode* ip, int times)
{
  char newpath[MAXPATH];

  if(times >= 10) {
    iunlock(ip);
    printf("follow too many times, maybe a cycle\n");
    return 0;
  }

  if(readi(ip, 0, (uint64)newpath, 0, sizeof(newpath)) == sizeof(newpath)) {
    iunlock(ip);

    if((ip = namei(newpath)) == 0)
      return ip;

    ilock(ip);
    if(ip->type == T_FILE) 
      return ip;
    return follow_symlink(ip, times+1);
  }
  return 0;
}

文章作者: AthenaCrafter
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 AthenaCrafter !
  目录