date: 2019-03-25 
tags: OS  6.828  
这里会记录阅读6.828课程lecture note的我的个人笔记。可能会中英混杂,不是很适合外人阅读,也请见谅。
我们为什么需要file system
file system有哪些有趣的点?
之后我们来看一下xv6的实现。因为lecture note里头的东西太乱了,不妨我们直接看xv6 book中对应的内容。

xv6的fs实现分为7层。从下向上其顺序和功能如下:
/usr/rtm/xv6/fs.c/usr/rtm/xv6/fs.c这样的地址接口buffer layer的代码位于bio.c其主要接口为bwrite和bread。
// Return a locked buf with the contents of the indicated block.
struct buf*
bread(uint dev, uint blockno) {
  struct buf *b;
  b = bget(dev, blockno);
  if((b->flags & B_VALID) == 0) {
    iderw(b);
  }
  return b;
}
// Write b's contents to disk.  Must be locked.
void
bwrite(struct buf *b)
{
  if(!holdingsleep(&b->lock))
    panic("bwrite");
  b->flags |= B_DIRTY;
  iderw(b);
}更深入一点的话,buf的结构和bget的实现如下:
// buf.h
struct buf {
  int flags;
  uint dev;
  uint blockno;
  struct sleeplock lock;
  uint refcnt;
  struct buf *prev; // LRU cache list
  struct buf *next;
  struct buf *qnext; // disk queue
  uchar data[BSIZE];
};
#define B_VALID 0x2  // buffer has been read from disk
#define B_DIRTY 0x4  // buffer needs to be written to disk
// bio.c
// Look through buffer cache for block on device dev.
// If not found, allocate a buffer.
// In either case, return locked buffer.
static struct buf*
bget(uint dev, uint blockno) {
  struct buf *b;
  acquire(&bcache.lock);
  // Is the block already cached?
  for(b = bcache.head.next; b != &bcache.head; b = b->next){
    if(b->dev == dev && b->blockno == blockno){
      b->refcnt++;
      release(&bcache.lock);
      acquiresleep(&b->lock);
      return b;
    }
  }
  // Not cached; recycle an unused buffer.
  // Even if refcnt==0, B_DIRTY indicates a buffer is in use
  // because log.c has modified it but not yet committed it.
  for(b = bcache.head.prev; b != &bcache.head; b = b->prev){
    if(b->refcnt == 0 && (b->flags & B_DIRTY) == 0) {
      b->dev = dev;
      b->blockno = blockno;
      b->flags = 0;
      b->refcnt = 1;
      release(&bcache.lock);
      acquiresleep(&b->lock);
      return b;
    }
  }
  panic("bget: no buffers");
}注意这里bget就是从链表里头取一个空的,并不会做MRU (most recent used)的删除操作,如果没有空的就报错。而这个释放的操作是用brelse来进行的。
// Release a locked buffer.
// Move to the head of the MRU list.
void
brelse(struct buf *b) {
  if(!holdingsleep(&b->lock))
    panic("brelse");
  releasesleep(&b->lock);
  acquire(&bcache.lock);
  b->refcnt--;
  if (b->refcnt == 0) {
    // no one is waiting for it.
    b->next->prev = b->prev;
    b->prev->next = b->next;
    b->next = bcache.head.next;
    b->prev = &bcache.head;
    bcache.head.next->prev = b;
    bcache.head.next = b;
  }
  
  release(&bcache.lock);
}大致就是把引用计数清空,并把这个buf移到链表最前面。
常见的一个crash recovery的方法是写logging。我们先来看一下xv6的logging,再来了解linux的EXT3