MIT xv6 2020系列实验:Lab8 lock

锁实验。锁算是比较简单的一个实验,之前了解过openmp就有了一个类似窃取的概念,那在这个实验里同样也是窃取,只需要对于每个CPU分配的内存块/ cache块进行维护即可。

kmem:

将一个kmem freelist 拓展为 NCPU 个即可。

修改init部分:一个初始化改为NCPU个初始化。

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

kfree修改:

void
kfree(void *pa)
{
  struct run *r;
  uint cid;
  push_off();
  cid=cpuid();
  pop_off();

  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;

  acquire(&kmems[cid].lock);
  r->next = kmems[cid].freelist;
  kmems[cid].freelist = r;
  release(&kmems[cid].lock);
}

直接从当前CPU获取一个空闲块,也即kmems[cpuid].freelist

kalloc同理:

void *
kalloc(void)
{
  struct run *r;
  uint cid;
  push_off();
  cid=cpuid();
  pop_off();

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

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

接下来是bcache部分:

#define BUCKETCNT 13

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

struct {
  struct spinlock lock;

  // 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;
} bcachelist[BUCKETCNT];

初始化改为序列初始化:

void
binit(void)
{
  struct buf *b;
  int i;

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

  // Create linked list of buffers
  // bcache.head.prev = &bcache.head;
  // bcache.head.next = &bcache.head;
  for(i = 0; i < BUCKETCNT; i++){
    initlock(&bcachelist[i].lock, "bcache_hash");
    bcachelist[i].head.prev = &bcachelist[i].head;
    bcachelist[i].head.next = &bcachelist[i].head;
  }
  for(i=0,b = bcache.buf; b < bcache.buf+NBUF; b++,i=(i+1)%BUCKETCNT){
    b->next = bcachelist[i].head.next;
    b->prev = &bcachelist[i].head;
    initsleeplock(&b->lock, "buffer");
    bcachelist[i].head.next->prev = b;
    bcachelist[i].head.next = b;

    b->now_hash = i;
  }
}

get部分改为序列化,第一遍在特定块内用遍历搜索,搜到对应块则返回。

否则uncached,再进行窃取,以lru方式查找到最近最少使用且未被使用的块,调换到当前blockno下进行处理。

static struct buf*
bget(uint dev, uint blockno)
{
  struct buf *b;
  struct buf *lru=0;
  uint hash = blockno%BUCKETCNT;
  
  // printf("bget...\n");

  acquire(&bcachelist[hash].lock);

  // Is the block already cached?
  for(b = bcachelist[hash].head.next; b != &bcachelist[hash].head; b = b->next){
  // printf("bget...??%p %p %p\n",b,b->next,&bcachelist[hash].head);
    if(b->dev == dev && b->blockno == blockno){
      b->refcnt++;
      release(&bcachelist[hash].lock);
      acquiresleep(&b->lock);
      return b;
    }
  }

  // Not cached.
  // Recycle the least recently used (LRU) unused buffer.
  for(int i=hash,flag = 1;flag || i!=hash;i=(i+1)%BUCKETCNT){
    flag = 0;
  // printf("bget...%d\n",i);
    acquire(&bcachelist[i].lock);
    for(b = bcachelist[i].head.prev; b != &bcachelist[i].head; b = b->prev){
  // printf("bget...%d\n",i);
      if(b->refcnt == 0) {
        lru = b;
        break;
      }
    }
    if(!lru){
      release(&bcachelist[i].lock);
      continue;
    }
    b=lru;
    b->next->prev = b->prev;
    b->prev->next = b->next;
    release(&bcachelist[i].lock);

    
    b->dev = dev;
    b->blockno = blockno;
    b->valid = 0;
    b->refcnt = 1;
    b->now_hash = hash;

    b->next = bcachelist[hash].head.next;
    b->prev = &bcachelist[hash].head;

    acquiresleep(&b->lock);

    bcachelist[hash].head.next->prev = b;
    bcachelist[hash].head.next = b;
    release(&bcachelist[hash].lock);
    return b;
  }

  if(!lru)
    panic("bget: no buffers");
  return lru;
}

然后给辅助函数加上标号:

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

  acquire(&bcachelist[b->now_hash].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 = bcachelist[b->now_hash].head.next;
    b->prev = &bcachelist[b->now_hash].head;
    bcachelist[b->now_hash].head.next->prev = b;
    bcachelist[b->now_hash].head.next = b;
  }
  
  release(&bcachelist[b->now_hash].lock);
}

void
bpin(struct buf *b) {
  acquire(&bcachelist[b->now_hash].lock);
  b->refcnt++;
  release(&bcachelist[b->now_hash].lock);
}

void
bunpin(struct buf *b) {
  acquire(&bcachelist[b->now_hash].lock);
  b->refcnt--;
  release(&bcachelist[b->now_hash].lock);
}

即可通过测试。

posted @   Thaudmin  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示