6.S081|Lab8-File-system
本文最后更新于 69 天前,其中的信息可能已经有所发展或是发生改变。

概念引入

xv6的文件系统分为七层,自底向上的顺序是硬盘(Disk),缓冲区高速缓存(Buffer cache),日志(Logging),索引节点(Inode),目录(Directory),路径名(Pathname),文件描述符(File descriptor)

inode

inode是一个文件或者目录的元数据,inode包含了关于文件的所有元数据,包括文件类型、权限、大小和指向文件数据块的指针。inode里面存在着许多block,指向数据块真正存在的地方。但是这样会产生一个问题,一个inode里的block没被用完,这样就会导致空间浪费。这被称为内部碎片(internal fragmentation)。

dinode和inode的关系

dinode是inode储存在磁盘上的版本,简化了一些只在操作inode时需要的字段。相应的,inode时dinode被加载到内存中的版本,多了一些可以辅助inode操纵的字段,比如锁lock,引用计数ref等

buffercache区和内存中的inode

当文件系统需要获取某个文件的 inode 时,iget 函数会通过 bread 从磁盘读取包含该 inode 的磁盘块,然后从中提取出 inode 的信息,并将其加载到内存中的 inode 结构里。

例如,假设我们需要读取 inode 编号为 5 的 inode,它在磁盘上位于 inode 区的一个块中。iget 会通过 bread 从磁盘中读取包含 inode 5 的整个块,这个块会被缓存到 buffercache 中。然后,iget 从缓冲区中的数据提取出 dinode 信息,并填充到内存中的 inode 结构中(也就是 struct inode)。

这一步的具体流程如下:

  • iget 根据 inum 计算出 inode 所在的磁盘块。

  • 调用 bread,从 buffercache 中获取包含该 inode 的块(如果 buffercache 中没有该块,就从磁盘读取并缓存)。

  • 从 buffercache 返回的缓冲块中提取 inode 信息,填充到内存中的 inode 结构中。

关于inode并发问题

暂存。。。以后来补

file descriptor与inode

inodefile 的关系

  • inode 是文件的底层数据结构,描述文件的属性和位置,是整个系统中对文件的唯一标识。
  • file 是对进程而言的高层次文件描述符,记录每个进程对文件的访问信息。file 结构体包含一个 ip 指针,用于指向文件的 inode,从而通过 inode 获取文件内容。
  • 一个文件可以有多个 file 结构体,但它们会共享同一个 inode
    • 例如,如果两个进程同时打开同一个文件,每个进程会有自己的 file 结构(包含独立的偏移量等信息),但它们都指向同一个 inode

Large files(moderate)

完成这个task首先要理解数据块是如何存储在inode中的,这里直接给出示意图。

Pasted image 20241110000437.png

#define FSMAGIC 0x10203040

#define NDIRECT 11 // 修改这里,因为我们减少一个直接快,新增一个二级间接块
#define NINDIRECT (BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + NINDIRECT + NINDIRECT * NINDIRECT) // 这里记得要改,不然系统识别不出来新增的大小

// 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+2];   // Data block addresses 总大小依旧是13
};
//kernel/fs.c
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){
    // 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;
  }
  
// new code START
  bn -= NINDIRECT;

  if(bn < NINDIRECT * NINDIRECT) {
    // Allocate double indirect block if necessary
    if((addr = ip->addrs[NDIRECT+1]) == 0)
      ip->addrs[NDIRECT+1] = addr = balloc(ip->dev);
    
    // Load the first level indirect block
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    uint first_index = bn / NINDIRECT;  // Calculate first level index
    if((addr = a[first_index]) == 0) {
      a[first_index] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);

    // Load the second level indirect block
    bp = bread(ip->dev, addr);
    a = (uint*)bp->data;
    bn = bn % NINDIRECT;  // Calculate second level index
    if((addr = a[bn]) == 0) {
      a[bn] = addr = balloc(ip->dev);
      log_write(bp);
    }
    brelse(bp);

    return addr;
  }
  // new code END
  
  panic("bmap: out of range");
}
//kernel/fs.c
void
itrunc(struct inode *ip)
{
  int i, j;
  struct buf *bp;
  uint *a, *b;

  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;
  }

// new code START
  if(ip->addrs[NDIRECT + 1]) {
    bp = bread(ip->dev, ip->addrs[NDIRECT + 1]);
    a = (uint*)bp->data;

    for(i = 0; i < NINDIRECT; i++) {
      if(a[i]) {
        struct buf *bp2 = bread(ip->dev, a[i]);
        b = (uint*)bp2->data;
        for(j = 0; j < NINDIRECT; j++) {
          if(b[j])
            bfree(ip->dev, b[j]);
        }
        brelse(bp2);
        bfree(ip->dev, a[i]);
      }
    }

    brelse(bp);
    bfree(ip->dev, ip->addrs[NDIRECT + 1]);
    ip->addrs[NDIRECT + 1] = 0;
  }
// new code END

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

Symbolic links(moderate)

sys_link,sys_symlink 和带有O_CREATE 的open的区别。

Sys_link为现有inode创建一个新名称。函数createkernel/sysfile.c:242)为新inode创建一个新名称。它是三个文件创建系统调用的泛化:带有O_CREATE标志的open生成一个新的普通文件

一些函数的分析过程

理解了这些可能会对做lab有些帮助。

sys_link()分析过程

uint64
sys_link(void)
{
  char name[DIRSIZ], new[MAXPATH], old[MAXPATH];
  struct inode *dp, *ip;

  if(argstr(0, old, MAXPATH) < 0 || argstr(1, new, MAXPATH) < 0)
    return -1;

  begin_op(); // 开始事务
  if((ip = namei(old)) == 0){ // namei的时候就会增加inode的引用了,在 namei 函数调用后,返回的 inode ip 的引用计数已经被增加了。这是 namei 的内部机制:每次成功找到并返回一个 inode 时,namei 都会调用 iget 函数来增加引用计数,确保这个 inode 在使用过程中不会被其他操作删除。
    end_op();
    return -1;
  }

  ilock(ip); // 开始修开inode,要先加锁
  if(ip->type == T_DIR){ // 目录的话,就失效
    iunlockput(ip); // 释放ip锁,并减少引用
    end_op();
    return -1;
  }

  ip->nlink++;
  iupdate(ip); // 更新数据到磁盘
  iunlock(ip); // 释放inode锁

  if((dp = nameiparent(new, name)) == 0) // 找到当前inode的父目录,并且找到其名字赋给name
    goto bad;
  ilock(dp); // 接下来要操作dp,所以先加锁
  if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){ // 判断是否操作的是一个设备,是否能够将新的硬链接条目添加到父目录 dp 中。dirlink(dp, name, ip->inum) 的作用是将 name 作为一个新的目录项插入到目录 dp 中,并将其指向文件 ip->inum(即原文件的 inode 号)
    iunlockput(dp);
    goto bad;
  }
  iunlockput(dp); // 是对父目录 inode 的操作,解锁并减少其引用计数,因为已经完成了所有操作
  iput(ip); // 是对被链接的目标文件 inode 的操作,减少其引用计数,我们已经完成了对inode的所有操作

  end_op(); // 结束事务

  return 0;

bad: // 恢复之前进行过的操作
  ilock(ip);
  ip->nlink--;
  iupdate(ip);
  iunlockput(ip);
  end_op();
  return -1;
}

sys_open()分析过程。这个函数在本次lab中非常重要。但是后面我们还会对这个函数做改动,所以留到后面一起分析。

namex()分析过程

static struct inode*
namex(char *path, int nameiparent, char *name)
{
  struct inode *ip, *next;

  if(*path == '/') // 判断是否是绝对路径
    ip = iget(ROOTDEV, ROOTINO);
  else
    ip = idup(myproc()->cwd); // ip指向当前目录

  while((path = skipelem(path, name)) != 0){ // 进入循环,一层一层解析。skipelem 函数从路径中提取一个元素,并将剩余的路径返回。例如,对路径 /a/b/c 第一次调用 skipelem 会提取 a,并将 path 设置为 "/b/c",将 name 设置为 "a"
    ilock(ip);
    if(ip->type != T_DIR){
      iunlockput(ip);
      return 0;
    }
    if(nameiparent && *path == '\0'){ // 如果nameiparent为真且path当前没有解析到结尾
      // Stop one level early.
      iunlock(ip);
      return ip;
    }
    if((next = dirlookup(ip, name, 0)) == 0){ // 在当前目录中寻找匹配的名字
      iunlockput(ip);
      return 0;
    }
    iunlockput(ip); // 对ip的使用结束,释放所,引用-1
    ip = next; // 更新ip
  }
  if(nameiparent){
    iput(ip);
    return 0;
  }
  return ip;
}

接下来我们正式开始做lab
先按照添加系统调用的方法,注册好symlink()系统调用。然后根据提示加上一些定义。

//kernel/stat.h
#define T_DIR     1   // Directory
#define T_FILE    2   // File
#define T_DEVICE  3   // Device
#define T_SYMLINK 4   // 新加的!

//kernel/fcntl.h
#define O_RDONLY  0x000
#define O_WRONLY  0x001
#define O_RDWR    0x002
#define O_CREATE  0x200
#define O_TRUNC   0x400
#define O_NOFOLLOW 0x800 // 新加的

接下来是主要逻辑实现的地方。

//kernel/sysfile.c
uint64
sys_symlink(void){

char target[MAXPATH], path[MAXPATH];
struct inode* i;


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

// printf("target is %s \n", target);
// printf("path is %s \n", path);

begin_op();

i = create(path, T_SYMLINK,0, 0); // 创建一个软连接类型的文件

if(i == 0) {
  end_op();
  return -1;
}

if (writei(i,0,(uint64)target,0,strlen(target)) < 0) { // 往文件里面写路径,待会我们读的时候就要利用写进去的路径读
end_op();
return -1;
}; 

iunlockput(i);

end_op();


return 0;
}

因为我们添加了一个新的文件类型,所以我们也要修改open函数来支持这个新的文件类型。

uint64
sys_open(void)
{
  char path[MAXPATH]; // 存储文件路径的字符串
  int fd, omode; // 文件描述符(fd)和打开模式(omode)
  struct file *f; // 文件结构体指针
  struct inode *ip; // inode 指针
  int n;

  // 获取系统调用的参数:文件路径和打开模式
  if((n = argstr(0, path, MAXPATH)) < 0 || argint(1, &omode) < 0)
    return -1; // 获取失败则返回 -1

  begin_op(); // 开始文件系统事务,以确保文件系统操作的原子性

  // 如果打开模式中包含 O_CREATE 标志,则创建文件
  if(omode & O_CREATE){
    ip = create(path, T_FILE, 0, 0); // 调用 create 创建新文件的 inode
    if(ip == 0){ // 创建失败则结束事务并返回 -1
      end_op();
      return -1;
    }
  } else {
    // 如果不需要创建文件,则尝试查找文件路径对应的 inode
    if((ip = namei(path)) == 0){ // 如果文件不存在,则返回 -1
      end_op();
      return -1;
    }
    ilock(ip); // 锁定 inode,以确保并发访问安全
    // 如果文件类型为目录且打开模式不是只读,则返回错误
    if(ip->type == T_DIR && omode != O_RDONLY){
      iunlockput(ip); // 解锁并释放 inode
      end_op();
      return -1;
    }
  }

  // 处理符号链接(symbolic link)
  if(ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)) { // 若符号链接未被禁止跟随
    int MAX_SYMLINK_DEPTH = 10; // 设置符号链接的最大跟随深度
    for(int i = 0; i < MAX_SYMLINK_DEPTH; ++i) {
      // 读取符号链接指向的路径
      if(readi(ip, 0, (uint64)path, 0, MAXPATH) != MAXPATH) {
        iunlockput(ip); // 读取失败则解锁并释放 inode
        end_op();
        return -1;
      }
      iunlockput(ip); // 释放当前符号链接 inode 锁
      ip = namei(path); // 获取符号链接指向的路径的 inode
      if(ip == 0) { // 若符号链接路径不存在则返回错误
        end_op();
        return -1;
      }
      ilock(ip); // 锁定新的 inode
      if(ip->type != T_SYMLINK) // 如果找到的文件不是符号链接,停止递归
        break;
    }
    // 如果超过最大深度仍然是符号链接,返回错误
    if(ip->type == T_SYMLINK) {
      iunlockput(ip);
      end_op();
      return -1;
    }
  }

  // 检查是否为有效的设备文件,若设备号无效,则返回错误
  if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
    iunlockput(ip);
    end_op();
    return -1;
  }

  // 分配文件结构(file struct)和文件描述符
  if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
    if(f) // 若分配了文件结构体但失败,则关闭该文件结构体
      fileclose(f);
    iunlockput(ip); // 解锁并释放 inode
    end_op();
    return -1;
  }

  // 初始化文件结构体(file struct)中的信息
  if(ip->type == T_DEVICE){ // 若文件类型为设备
    f->type = FD_DEVICE;
    f->major = ip->major; // 记录设备号
  } else { // 若文件类型为普通文件或目录
    f->type = FD_INODE;
    f->off = 0; // 文件偏移设置为 0(从文件头开始)
  }
  f->ip = ip; // 设置文件的 inode 指针
  f->readable = !(omode & O_WRONLY); // 如果不是只写,则设置为可读
  f->writable = (omode & O_WRONLY) || (omode & O_RDWR); // 设置是否可写

  // 如果打开模式包含 O_TRUNC,并且文件类型是普通文件,则截断文件
  if((omode & O_TRUNC) && ip->type == T_FILE){
    itrunc(ip); // 调用 itrunc 截断文件内容
  }

  iunlock(ip); // 解锁 inode
  end_op(); // 结束文件系统事务

  return fd; // 返回文件描述符
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇