The buffer cache is a linked list of buf structures holding cached copies of disk block contents.
Caching disk blocks in memory reduces the number of disk reads and also provides a synchronization point for disk blocks used by multiple processes.
Role
1. Sync access to disk blocks to ensure only one copy of a block in memory and only one kernel thread can use this copy.
2. Cache popular blocks See bio.c
Source code
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 liststruct buf *next; uchar data[BSIZE];};
struct {struct spinlock lock;struct buf buf[NBUF];// Linked list of all buffers, through prev/next.// head.next is most recently used.struct buf head;} bcache;
buffer cache impl.
struct {struct spinlock lock;struct buf buf[NBUF];// Linked list of all buffers, through prev/next.// head.next is most recently used.struct buf head;} bcache;voidbinit(void){struct buf *b;initlock(&bcache.lock,"bcache");// Create linked list of buffersbcache.head.prev =&bcache.head;bcache.head.next =&bcache.head;for(b =bcache.buf; b <bcache.buf+NBUF; b++){b->next =bcache.head.next;b->prev =&bcache.head;initsleeplock(&b->lock,"buffer");bcache.head.next->prev = b;bcache.head.next = b; }}// Look through buffer cache for block on device dev.// If not found, allocate a buffer.// In either case, return locked buffer.staticstruct buf*bget(uint dev,uint blockno){struct buf *b;acquire(&bcache.lock);// Is the block already cached?for(b =bcache.head.next; b !=&bcache.head; b =b->next){if(b->dev == dev &&b->blockno == blockno){b->refcnt++;release(&bcache.lock);acquiresleep(&b->lock);return b; } }// Not cached; recycle an unused buffer.for(b =bcache.head.prev; b !=&bcache.head; b =b->prev){if(b->refcnt ==0) {b->dev = dev;b->blockno = blockno;b->valid =0;b->refcnt =1;release(&bcache.lock);acquiresleep(&b->lock);return b; } }panic("bget: no buffers");}// Return a locked buf with the contents of the indicated block.struct buf*bread(uint dev,uint blockno){struct buf *b; b =bget(dev, blockno);if(!b->valid) {virtio_disk_rw(b->dev, b,0);b->valid =1; }return b;}// Write b's contents to disk. Must be locked.voidbwrite(struct buf *b){if(!holdingsleep(&b->lock))panic("bwrite");virtio_disk_rw(b->dev, b,1);}// Release a locked buffer.// Move to the head of the MRU list.voidbrelse(struct buf *b){if(!holdingsleep(&b->lock))panic("brelse");releasesleep(&b->lock);acquire(&bcache.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.head.next;b->prev =&bcache.head;bcache.head.next->prev = b;bcache.head.next = b; }release(&bcache.lock);}voidbpin(struct buf *b) {acquire(&bcache.lock);b->refcnt++;release(&bcache.lock);}voidbunpin(struct buf *b) {acquire(&bcache.lock);b->refcnt--;release(&bcache.lock);}