MIT xv6 2020系列實驗:Lab8 lock

Thaudmin發表於2024-12-09

鎖實驗。鎖算是比較簡單的一個實驗,之前瞭解過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);
}
```cpp

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


即可透過測試。

相關文章