Loading

6.S081 lab8 locks

#Memory allocator

  • 为每个CPU设置一个锁和空闲物理页链表

    image.png

  • kinit中初始化所有CPU的锁

    image.png

  • 修改kalloc,用cpuid获取当前的CPU核心编号,并且获取该CPU的锁。查看该CPU的空闲物理页链表是否为空,若不为空,直接分配一个物理页即可,若为空,则扫描其余CPU核的空闲物理页链表,“窃取”一个物理页。扫描时也应获取锁,因为该操作会修改链表指针。

    void *
    kalloc(void)
    {
      struct run *r;
    
      push_off();
      int num = cpuid();
    
      acquire(&kmem[num].lock);
      r = kmem[num].freelist;
      if(r)
        kmem[num].freelist = r->next;
      else {
        for (int i = 0; i < NCPU; i++){
          if (i == num) continue;
          if (kmem[i].freelist){
            acquire(&kmem[i].lock);
            r = kmem[i].freelist;
            kmem[i].freelist = kmem[i].freelist->next;
            release(&kmem[i].lock);
            break;
          }
        }
      }
      release(&kmem[num].lock);
      pop_off();
    
      if(r)
        memset((char*)r, 5, PGSIZE); // fill with junk
      return (void*)r;
    }
    

#Buffer cache

在这个实验中我没有用时间戳来选择最近最少使用的块,仍然使用的是双向循环链表,测试依然可以过,如果有兴趣的话可以试试。这个实验非常注重获取锁、释放锁的搭配,如果漏写了的话很容易造成死锁。

  • 定义宏NBUCKETS表示哈希桶的数量

    image.png

    修改bcache,加入NBUCKETS个哈希桶

    image.png

  • binit中初始化所有哈希桶的锁以及双向循环链表

    void
    binit(void)
    {
      struct buf *b;
    
      /* initlock(&bcache.lock, "bcache"); */
      // 初始化哈希组的锁
      initlock(&bcache.lock, "bcache");
      for (int i = 0; i < NBUCKETS; i++)
        initlock(&bcache.hashbucket[i].lock, "bcache");
    
      // Create linked list of buffers
      /* bcache.head.prev = &bcache.head; */
      /* bcache.head.next = &bcache.head; */
      // 初始化哈希组双链表
      for (int i = 0; i < NBUCKETS; i++){
        bcache.hashbucket[i].head.prev = &bcache.hashbucket[i].head;
        bcache.hashbucket[i].head.next = &bcache.hashbucket[i].head;
      }
    
      // 先将全部buf映射到0号哈希队列中
      for(b = bcache.buf; b < bcache.buf+NBUF; b++){
        b->next = bcache.hashbucket[0].head.next;
        b->prev = &bcache.hashbucket[0].head;
        initsleeplock(&b->lock, "buffer");
        bcache.hashbucket[0].head.next->prev = b;
        bcache.hashbucket[0].head.next = b;
      }
    }
    
  • 修改bgetbget的作用是根据设备号dev和块号blocknobcache中找是否存在相应的块,若存在,则返回该块。若不存在,则分配一个空闲块并返回。

    static struct buf*
    bget(uint dev, uint blockno)
    {
      struct buf *b;
      // 哈希映射
      int idx = blockno % NBUCKETS;
    
      acquire(&bcache.hashbucket[idx].lock);
      /* printf("bget获取%d号桶的锁\n", idx); */
      /* for (b = bcache.hashbucket[idx].head.next; b != &bcache.hashbucket[idx].head; b = b->next){ */
      /*   printf("%d", b->blockno); */
      /* } */
    
      // Is the block already cached?
      for(b = bcache.hashbucket[idx].head.next; b != &bcache.hashbucket[idx].head; b = b->next){
        if(b->dev == dev && b->blockno == blockno){
          b->refcnt++;
          release(&bcache.hashbucket[idx].lock);
          /* printf("已经在idx号中映射了,释放%d号桶的锁\n", idx); */
          acquiresleep(&b->lock);
          return b;
        }
      }
    
      /* printf("block num %d->%d 失败\n", blockno, idx); */
      // Not cached.
      // Recycle the least recently used (LRU) unused buffer.
      // 先在idx哈希队列中找空闲的buf
      for (b = bcache.hashbucket[idx].head.prev; b != &bcache.hashbucket[idx].head; b = b->prev){
          if(b->refcnt == 0) {
            b->dev = dev;
            b->blockno = blockno;
            b->valid = 0;
            b->refcnt = 1;
            release(&bcache.hashbucket[idx].lock);
            acquiresleep(&b->lock);
            return b;
          }
      }
    
      // 需要从其余的哈希桶中窃取一个空闲的buf
      /* acquire(&bcache.lock);  // 防止A steal from B, B steal from A的死锁情况 */
      for (int i = (idx + 1) % NBUCKETS; i != idx; i = (i + 1) % NBUCKETS){
        acquire(&bcache.hashbucket[i].lock);
        /* printf("获取%d号桶的锁\n", i); */
        for(b = bcache.hashbucket[i].head.prev; b != &bcache.hashbucket[i].head; b = b->prev){
          if(b->refcnt == 0) {
            /* printf("find in bucket %d num:%d\n", i,blockno); */
            b->dev = dev;
            b->blockno = blockno;
            b->valid = 0;
            b->refcnt = 1;
            b->prev->next = b->next;
            b->next->prev = b->prev;
            release(&bcache.hashbucket[i].lock);
            b->next = bcache.hashbucket[idx].head.next;
            b->prev = &bcache.hashbucket[idx].head;
            bcache.hashbucket[idx].head.next->prev = b;
            bcache.hashbucket[idx].head.next = b;
            /* printf("释放了%d号锁\n", idx); */
            release(&bcache.hashbucket[idx].lock);
            /* release(&bcache.lock); */
            acquiresleep(&b->lock);
            return b;
          }
        }
        release(&bcache.hashbucket[i].lock);
        /* printf("释放%d号桶的锁\n", i); */
      }
      panic("bget: no buffers");
    }
    

    首先通过取余运算获得哈希函数值,也就知道了块号为blockno的磁盘块所在的哈希桶。之后获取哈希桶锁,并遍历该哈希桶查找是否有符合条件的缓冲块。若找不到,则重新遍历该哈希桶,查找是否有空闲的缓冲块(通过引用计数refcnt判断),若该哈希桶中不存在空闲的缓冲块,则从其余哈希桶中“窃取”。

    static struct buf*
    bget(uint dev, uint blockno)
    {
      struct buf *b;
      // 哈希映射
      int idx = blockno % NBUCKETS;
    
      acquire(&bcache.hashbucket[idx].lock);
      /* printf("bget获取%d号桶的锁\n", idx); */
      /* for (b = bcache.hashbucket[idx].head.next; b != &bcache.hashbucket[idx].head; b = b->next){ */
      /*   printf("%d", b->blockno); */
      /* } */
    
      // Is the block already cached?
      for(b = bcache.hashbucket[idx].head.next; b != &bcache.hashbucket[idx].head; b = b->next){
        if(b->dev == dev && b->blockno == blockno){
          b->refcnt++;
          release(&bcache.hashbucket[idx].lock);
          /* printf("已经在idx号中映射了,释放%d号桶的锁\n", idx); */
          acquiresleep(&b->lock);
          return b;
        }
      }
    
      /* printf("block num %d->%d 失败\n", blockno, idx); */
      // Not cached.
      // Recycle the least recently used (LRU) unused buffer.
      // 先在idx哈希队列中找空闲的buf
      for (b = bcache.hashbucket[idx].head.prev; b != &bcache.hashbucket[idx].head; b = b->prev){
          if(b->refcnt == 0) {
            b->dev = dev;
            b->blockno = blockno;
            b->valid = 0;
            b->refcnt = 1;
            release(&bcache.hashbucket[idx].lock);
            acquiresleep(&b->lock);
            return b;
          }
      }
    
      // 需要从其余的哈希桶中窃取一个空闲的buf
      /* acquire(&bcache.lock);  // 防止A steal from B, B steal from A的死锁情况 */
      for (int i = (idx + 1) % NBUCKETS; i != idx; i = (i + 1) % NBUCKETS){
        acquire(&bcache.hashbucket[i].lock);
        /* printf("获取%d号桶的锁\n", i); */
        for(b = bcache.hashbucket[i].head.prev; b != &bcache.hashbucket[i].head; b = b->prev){
          if(b->refcnt == 0) {
            /* printf("find in bucket %d num:%d\n", i,blockno); */
            b->dev = dev;
            b->blockno = blockno;
            b->valid = 0;
            b->refcnt = 1;
            b->prev->next = b->next;
            b->next->prev = b->prev;
            release(&bcache.hashbucket[i].lock);
            b->next = bcache.hashbucket[idx].head.next;
            b->prev = &bcache.hashbucket[idx].head;
            bcache.hashbucket[idx].head.next->prev = b;
            bcache.hashbucket[idx].head.next = b;
            /* printf("释放了%d号锁\n", idx); */
            release(&bcache.hashbucket[idx].lock);
            /* release(&bcache.lock); */
            acquiresleep(&b->lock);
            return b;
          }
        }
        release(&bcache.hashbucket[i].lock);
        /* printf("释放%d号桶的锁\n", i); */
      }
      panic("bget: no buffers");
    }
    

    在更改双循环链表的指针时一定要仔细。当初做实验时就是这里写错了导致debug了好久。。

  • 更改brelse,该函数的作用是释放缓冲块,若引用计数减为0,则说明该缓冲块不会再使用,将该缓冲块移动至双向循环链表的最前方(LRU)

    void
    brelse(struct buf *b)
    {
      if(!holdingsleep(&b->lock))
        panic("brelse");
    
      releasesleep(&b->lock);
      
      int idx = b->blockno % NBUCKETS;
      acquire(&bcache.hashbucket[idx].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.hashbucket[idx].head.next;
        b->prev = &bcache.hashbucket[idx].head;
        bcache.hashbucket[idx].head.next->prev = b;
        bcache.hashbucket[idx].head.next = b;
      }
      
      release(&bcache.hashbucket[idx].lock);
    }
    
posted @ 2022-01-18 11:07  Kyoz1  阅读(60)  评论(0)    收藏  举报
7