6.S081 lab8 locks
#Memory allocator
-
为每个CPU设置一个锁和空闲物理页链表
-
在
kinit
中初始化所有CPU的锁 -
修改
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
表示哈希桶的数量修改
bcache
,加入NBUCKETS
个哈希桶 -
在
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; } }
-
修改
bget
,bget
的作用是根据设备号dev
和块号blockno
在bcache
中找是否存在相应的块,若存在,则返回该块。若不存在,则分配一个空闲块并返回。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); }