MIT-6.s081-CodeWalk-fs.c&file.c&sysfile.c

这篇博客深入探讨了Xv6操作系统的缓冲区缓存层、inode层以及文件系统的工作原理。内容涵盖balloc和Bfree的实现,Ialloc和Iget操作,以及inode的锁定和释放。此外,还详细解析了目录层的结构,如dirent和dirlookup函数,以及文件描述符层的file.c和sysfile.c中的关键函数,如sys_link和Create。文章通过代码分析阐述了如何在Xv6中高效地管理数据块、inode和文件系统操作。
摘要由CSDN通过智能技术生成

1. Buffer cache layer

balloc

要分配一个新的block,就需要根据bitmap了解block的free/allocated情况。因此,可以遍历data block,对于每一个data block查看是否是free,如果是就直接返回这个block就可以了。
在Xv6中,每个data block都去查找一次bitmap block这样效率其实很低,因为data block只是bitmap block的one bit,这时候如果正好把整个bitmap block的所有bits都检查一遍,效率很提升很多。
下面代码中,外层循环b表示一个data block,遍历每一个data block(从下标0开始),通过BBLOCK(b, sb)计算出data block b是bitmap block的第几个block。由于我们每次把一整个bitmap block都检查完,所以相当于一次检查了BPB(8 * 1024)个data block,所以外层循环每次b递增BPB
BBLOCK的计算原理,之前在学校操作系统课上算的很溜,写代码这里有点愣了,正好复习一下。
如何计算一个No. B data block在Bitmap中是第几个block?
第一个Bitmap block起始位置的block + B
作为bitmap,一个block的大小是1024bytes,可以存储的bit有10248个block。因此 B / ( 1024 ∗ 8 ) B / (1024*8) B/(10248)就是应该在bitmap的哪一个block。
一个bitmap block可以有1024
8个bits就可以代表这么多的data block。1024*8在Xv6代码中是BPB = (BSIZE * 8)
读取到bp后,内层循环bi表示一个bitmap bit,每次递增1。
由于bread返回的是有lock的buffer,所以这里也会防止race condition导致的死锁。最后通过brelse释放锁。

// Allocate a zeroed disk block.
static uint
balloc(uint dev)
{
   
  int b, bi, m;
  struct buf *bp;
  bp = 0;
  for(b = 0; b < sb.size; b += BPB){
   
    bp = bread(dev, BBLOCK(b, sb));
    for(bi = 0; bi < BPB && b + bi < sb.size; bi++){
   
      m = 1 << (bi % 8);
      if((bp->data[bi/8] & m) == 0){
     // Is block free?
        bp->data[bi/8] |= m;  // Mark block in use.
        log_write(bp);
        brelse(bp);
        bzero(dev, b + bi);
        return b + bi;
      }
    }
    brelse(bp);
  }
  panic("balloc: out of blocks");
}

Bfree

有了理解balloc的基础上,理解Bfree就更容易了。

  1. 读取对应bitmap block到buffer bp
  2. 找到data block b在bitmap block中对应的bit bi
  3. 使用mask方法把把bi对应的位置设置为0(free)。
  4. 调用log_write(bp)
  5. 释放bp
// Free a disk block.
static void
bfree(int dev, uint b)
{
   
  struct buf *bp;
  int bi, m;
  bp = bread(dev, BBLOCK(b, sb));
  bi = b % BPB;
  m = 1 << (bi % 8);
  if((bp->data[bi/8] & m) == 0)
    panic("freeing free block");
  bp->data[bi/8] &= ~m;
  log_write(bp);
  brelse(bp);
}

2. Inode Layer

Ialloc&Iget

Ialloc的作用是分配一个inode,inode在disk中存储的方式也是block,一个block的大小为1024bytes,struct dinode(disk中的inode形式)为8字节,所以一个block能存放128个inode的信息。

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

要分配一个inode,需要遍历disk的inode所有节点,查看是否有一个inode是free状态。

  1. 首先从disk中读取对应inum所在的block,经过IBLOCK进行计算inum在disk中的block位置
  2. 在kernel获取到bp指针后,从bp指针的data中获取在一个block里面所有struct dinode,通过IPB计算出当前inum对应的inode应该在哪个位置,dip指针指向该位置。
  3. 如果发现dip->type == 0说明这是一个free inode,在返回这个inode之前,必须要将disk的struct dinode转换成内存中的struct inode这两个结构是不一样的,调用iget(uint dev, uint inum)方法,会寻找这个inode是否已经在内存的有cache了,如果没有cache当前inode,直接寻找一个free的inode cache,写入inode信息到cache的inode,包含dev,inum,ref。但是不要lock,不要从disk读取,这是iget特殊的地方。这里有一个注意的地方就是ip->valid = 0而不是1,这里在下面的ilock会用到这个valid来从硬盘读取inode的信息。
// Inodes per block.
#define IPB           (BSIZE / sizeof(struct dinode))
// Block containing inode i
#define IBLOCK(i, sb)     ((i) / IPB + sb.inodestart)Frans Kaashoek, 6 years ago: • Pick up where i left off in april:
// Allocate an inode on device dev.
// Mark it as allocated by  giving it type type.
// Returns an unlocked but allocated and referenced inode.
struct inode*
ialloc(uint dev, short type)
{
   
  int inum;
  struct buf *bp;
  struct dinode *dip;
  for(inum = 1; inum < sb.ninodes; inum++){
   
    bp = bread(dev, IBLOCK(inum, sb));
    dip = (struct dinode*)bp->data + inum%IPB;
    if(dip->type == 0){
     // a free inode
      memset(dip, 0, sizeof(*dip));
      dip->type = type;
      log_write(bp);   // mark it allocated on the disk
      brelse(bp);
      return iget(dev, inum);
    }
    brelse(bp);
  }
  panic("ialloc: no inodes");
}
// Find the inode with number inum on device dev
// and return the in-memory copy. Does not lock
// the inode and does not read it from disk.
static struct inode*
iget(uint dev, uint inum)
{
   
  struct inode *ip, *empty;
  acquire(&itable.lock);
  // Is the inode already in the table?
  empty = 0;
  for(ip = &itable.inode[0]; ip < &itable.inode[NINODE]; ip++){
   
    if(ip->ref > 0 && ip->dev == dev && ip->inum == inum){
   
      ip->ref++;
      release(&itable.lock);
      return ip;
    }
    if(empty == 0 && ip->ref == 0)    // Remember empty slot.
      empty = ip;
  }
  // Recycle an inode entry.
  if(empty == 0)
    panic("iget: no inodes");
  ip = empty;
  ip->dev = dev;
  ip->inum = inum;
  ip->ref = 1;
  ip->valid = 0;
  release(&itable.lock);
  return ip;
}

void ilock(struct inode *ip)

  1. 想要读取或者修改inode的任何数据,必须先调用ilock方法,确保只有一个进程可以访问一个in-memory inode。
  2. ilock使用的是sleep lock。
  3. 如果inode->valid = 0,就需要从disk中读取信息。正好符合iget将inode->valid = 0match上。
  4. 解锁需要使用iunlock(struct inode *ip)
// Lock the given inode.
// Reads the inode from disk if necessary.
void
ilock(struct inode *ip)
{
   
  struct buf *bp;
  struct dinode *dip;
  if(ip == 0 || ip->ref < 1)
    panic("ilock");
  acquiresleep(&ip->lock);
  if(ip->valid == 0){
   
    bp = bread(ip->dev, IBLOCK(ip->inum, sb));
    dip = (struct dinode*)bp<
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值