mit6.s081 lab8 locks

Memory allocator

这个exericise的工作是通过为每个CPU维护一个freelist来减少kalloc时的锁竞争,当CPU对应的freelist为空时,从其他CPU的freelist中窃取page。
1.为每个CPU都维护一个freelist

struct {
  struct spinlock lock;
  struct run *freelist;
} kmem[NCPU];

2.kinit时为每个CPU对应freelist初始化锁

void
kinit()
{
  for (int i = 0; i < NCPU; i++)
    initlock(&kmem[i].lock, "kmem");
  freerange(end, (void*)PHYSTOP);
}

3.freerange时将所有的page都放到同一个CPU对应的freelist中,freerange函数保持不变
4.kalloc时获取当前cpu的cpuid,然后根据cpuid去对应的freelist中查找是否有可用的page,如果没有到其他cpu的freelist中查找

void *
kalloc(void)
{
  struct run *r;
  push_off();
  int cpu_id = cpuid(); 
  pop_off();

  acquire(&kmem[cpu_id].lock);
  r = kmem[cpu_id].freelist;
  if(r) 
    kmem[cpu_id].freelist = r->next;
  else {
    for (int i = 0; i < NCPU; i++) {
      if (i != cpu_id) {
        acquire(&kmem[i].lock);
        r = kmem[i].freelist;
        if(r) 
          kmem[i].freelist = r->next;
        release(&kmem[i].lock);
        if (r)
          break;
      }
    }
  } 

  release(&kmem[cpu_id].lock);

  if(r)
    memset((char*)r, 5, PGSIZE); // fill with junk
  return (void*)r;
}

4.kfree时将page返还到当前cpu的freelist中

void
kfree(void *pa)
{
  struct run *r;

  if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
    panic("kfree");

  // Fill with junk to catch dangling refs.
  memset(pa, 1, PGSIZE);

  r = (struct run*)pa;
  push_off();
  int cpu_id = cpuid(); 
  pop_off();

  acquire(&kmem[cpu_id].lock);
  r->next = kmem[cpu_id].freelist;
  kmem[cpu_id].freelist = r;
  release(&kmem[cpu_id].lock);
}
buffer cache
buffer cache的作用

1.确保一个disk block在内存只有一个副本,并且每次只被一个kernel thread使用
2.对被频繁使用的disk block进行缓存,从而减少对磁盘进行读操作的开销

实现

这个exercise的目的是减少bcache中的锁争用问题,xv6中的实现是将bcache中的buf组成一个链表,bget时从前往后查找看对应的dev和block是否存在对应的buffer,如果不存在对应的buffer则从后往前查找,返回首个引用计数为0的buffer。brelse中将释放的buffer插入到链表的头部,从而使得链表整体符合频繁使用的buffer在前的顺序。
为了减少锁争用,使用分BUCKET的方式,在实现中使用了7个BUCKET,通过对blockno进行哈希来获取到block对应的BUCKET。不使用原来的链表方案,而是使用ticks来记录buffer上一次的使用时间,如果block没有对应的cache,则将buf数组中最早使用的buffer返回给调用者。

1.在buf结构体中添加nticks成员记录buffer的使用时间

struct buf {
  int valid;   // has data been read from disk?
  int disk;    // does disk "own" buf?
  uint dev;
  uint blockno;
  struct sleeplock lock;
  uint refcnt;
  struct buf *prev; // LRU cache list
  struct buf *next;
  uchar data[BSIZE];
  uint nticks;
};

2.使用多个BUCKET,从而减少使用bcache时锁的竞争

#define NBUCKET 7

struct {
  struct spinlock lock;
  struct buf buf[NBUF];

  // 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.
} bcache[NBUCKET];

3.binit中初始化bcache中的锁以及每个buffer的锁,并对buffer进行初始化

void
binit(void)
{
  struct buf *b;

  for (int i = 0; i < NBUCKET; i++) {
    initlock(&bcache[i].lock, "bcache");
    for(b = bcache[i].buf; b < bcache[i].buf+NBUF; b++){
      initsleeplock(&b->lock, "buffer");
      b->refcnt = 0;
      b->nticks = 0;
    }
  }
}

4.bget时通过对blockno进行哈希获取对应的bucket,查找bucket中的是否存在对应的buffer,如果存在对应的buffer直接返回该buffer,如果不存在对应的buffer则在bucket中查找nticks最小且引用计数为0的buffer。

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

  int bucket_idx = blockno % NBUCKET;
  acquire(&bcache[bucket_idx].lock);

  int min_idx = -1;
  uint min_ticks = __UINT32_MAX__;

  // Is the block already cached?
  for(int i = 0; i < NBUF; i++){
    // 如果存在对应buffer,释放bcache的锁,增加buffer的引用以及获取buffer的锁
    b = bcache[bucket_idx].buf + i;
    if(b->dev == dev && b->blockno == blockno){
      b->refcnt++;
      release(&bcache[bucket_idx].lock);
      acquiresleep(&b->lock);
      return b;
    }
    else {
      // printf("refcnt: %d, ntick: %d\n", b->refcnt, b->nticks);
      if (b->refcnt == 0 && b->nticks < min_ticks) {
        min_idx = i;
        min_ticks = b->nticks;
      }
    }
  }

  // Not cached.
  // Recycle the least recently used (LRU) unused buffer.
  // 如果不存在对应的cache且找到可用buffer
  if (min_idx != -1) {
    b = bcache[bucket_idx].buf + min_idx;
    b->dev = dev;
    b->blockno = blockno;
    b->valid = 0;
    b->refcnt = 1;
    release(&bcache[bucket_idx].lock);
    acquiresleep(&b->lock);
    return b;
  }

  panic("bget: no buffers");
}

5.brelse中释放的操作修改为更新buffer的nticks

// 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 bucket_idx = b->blockno % NBUCKET;
  acquire(&bcache[bucket_idx].lock);
  b->refcnt--;
  // 如果引用为0,释放掉该buffer
  if (b->refcnt == 0) {
    // no one is waiting for it.
    // 将该buffer移到头节点
    b->nticks = ticks;
  }
  
  release(&bcache[bucket_idx].lock);
}

6.bpin和bunpin修改为通过对blockno哈希获取bucket对应的锁

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

void
bunpin(struct buf *b) {
  int bucket_idx = b->blockno % NBUCKET;
  acquire(&bcache[bucket_idx].lock);
  b->refcnt--;
  release(&bcache[bucket_idx].lock);
}

请添加图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值