Loading

6.S081-2021 locks

buffer cache

原来的实现是一个双向链表,每次进行文件的读写操作都要锁住整个表,显然大大降低了系统的并发度。

所以可以仿照Java中currentHashMap的思路,使用分段锁,每个hash桶上一个锁。

但是在进行缓存驱逐的时候需要注意死锁的情况。

比如我们哈希值为2的缓存块不存在并且哈希值为5的缓冲块不存在。这时候CPU1会先获取桶2的锁然后遍历所有的桶,遍历到5的时候,因为桶5的锁被另外一个CPU2持有。而CPU2获取桶5的锁之后,同样需要遍历所有的桶查找,这时候桶2又被CPU1持有。形成循环等待,从而造成死锁。

#include "types.h"
#include "param.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "riscv.h"
#include "defs.h"
#include "fs.h"
#include "buf.h"

#define NBUCKET 13

int hash(int no){
  return no%NBUCKET;
}

struct hashbuf{
  struct spinlock lock;
  struct buf head;
};

struct {
  struct spinlock lock;
  struct buf buf[NBUF];
  struct hashbuf bucket[NBUCKET];
  // Linked list of all buffers, through prev/next.
  // Sorted by how recently the buffer was used.
  // head.next is most recent, head.prev is least.
  //struct buf head;
} bcache;

void
binit(void)
{
  struct buf *b;

  initlock(&bcache.lock, "bcache");

  // 初始化散列桶的锁和头节点
  for(int i = 0; i<NBUCKET; i++){
    initlock(&bcache.bucket[i].lock,"bache");
    bcache.bucket[i].head.prev = &bcache.bucket[i].head;
    bcache.bucket[i].head.next = &bcache.bucket[i].head;
  }

  // 将所有的buf都先添加到0号桶
  for(b = bcache.buf; b < bcache.buf+NBUF; b++){
    b->next = bcache.bucket[0].head.next;
    b->prev = &bcache.bucket[0].head;
    initsleeplock(&b->lock, "buffer");
    bcache.bucket[0].head.next->prev = b;
    bcache.bucket[0].head.next = b;
  }
}

// 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);
  int index = hash(blockno);
  acquire(&bcache.bucket[index].lock);
  
  // Is the block already cached?
  for(b = bcache.bucket[index].head.next; b != &bcache.bucket[index].head; b = b->next){
    if(b->dev == dev && b->blockno == blockno){
      b->refcnt++;
      b->timestamp = ticks;
      release(&bcache.bucket[index].lock);
      // release(&bcache.lock);
      acquiresleep(&b->lock);
      return b;
    }
  }
  
  release(&bcache.bucket[index].lock);
  
  uint oldestTime = __UINT32_MAX__;
  struct buf *item;
  int bucketId = -1;
  for(int i=index,loop=0;loop<NBUCKET;loop++){
    i=(index+loop)%NBUCKET;
    acquire(&bcache.bucket[i].lock);
    
    for(item = bcache.bucket[i].head.next; item != &bcache.bucket[i].head; item = item->next){
      if(item->refcnt == 0 && item->timestamp < oldestTime){
        oldestTime = item->timestamp;
        b = item;
        bucketId = i;
      }
    }

    release(&bcache.bucket[i].lock);
  }

  if(oldestTime!=__UINT32_MAX__){
  
    // 驱逐
    acquire(&bcache.bucket[bucketId].lock);
    b->next->prev = b->prev;
    b->prev->next = b->next;
    release(&bcache.bucket[bucketId].lock);

      

    acquire(&bcache.bucket[index].lock);
    b->next = bcache.bucket[index].head.next;
    b->prev = &bcache.bucket[index].head;
    bcache.bucket[index].head.next->prev = b;
    bcache.bucket[index].head.next = b;
    
    b->dev = dev;
    b->blockno = blockno;
    b->valid = 0;
    b->refcnt = 1;
    b->timestamp = ticks;
    release(&bcache.bucket[index].lock);
    acquiresleep(&b->lock);
    return b; 
  } 
  panic("bget: no buffers");
}

// 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->valid) {
    virtio_disk_rw(b, 0);
    b->valid = 1;
  }
  return b;
}

// Write b's contents to disk.  Must be locked.
void
bwrite(struct buf *b)
{
  if(!holdingsleep(&b->lock))
    panic("bwrite");
  virtio_disk_rw(b, 1);
}

// Release a locked buffer.
// Move to the head of the most-recently-used list.
void
brelse(struct buf *b)
{
  if(!holdingsleep(&b->lock))
    panic("brelse");

  releasesleep(&b->lock);

  int index = hash(b->blockno);
  acquire(&bcache.bucket[index].lock);
  b->refcnt--;
  b->timestamp = ticks;
  
  release(&bcache.bucket[index].lock);
}

void
bpin(struct buf *b) {
  int index = hash(b->blockno);
  acquire(&bcache.bucket[index].lock);
  b->refcnt++;
  release(&bcache.bucket[index].lock);
}

void
bunpin(struct buf *b) {
  int index = hash(b->blockno);
  acquire(&bcache.bucket[index].lock);
  b->refcnt--;
  release(&bcache.bucket[index].lock);
}
posted @ 2022-06-04 22:49  AD_milk  阅读(38)  评论(0编辑  收藏  举报