| // Buffer cache. | |
| // | |
| // 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. | |
| // | |
| // Interface: | |
| // * To get a buffer for a particular disk block, call bread. | |
| // * After changing buffer data, call bwrite to write it to disk. | |
| // * When done with the buffer, call brelse. | |
| // * Do not use the buffer after calling brelse. | |
| // * Only one process at a time can use a buffer, | |
| // so do not keep them longer than necessary. | |
| // | |
| // The implementation uses two state flags internally: | |
| // * B_VALID: the buffer data has been read from the disk. | |
| // * B_DIRTY: the buffer data has been modified | |
| // and needs to be written to disk. | |
| 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; | |
| void | |
| binit(void) | |
| { | |
| struct buf *b; | |
| initlock(&bcache.lock, "bcache"); | |
| //PAGEBREAK! | |
| // Create linked list of buffers | |
| bcache.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. | |
| static struct 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. | |
| // Even if refcnt==0, B_DIRTY indicates a buffer is in use | |
| // because log.c has modified it but not yet committed it. | |
| for(b = bcache.head.prev; b != &bcache.head; b = b->prev){ | |
| if(b->refcnt == 0 && (b->flags & B_DIRTY) == 0) { | |
| b->dev = dev; | |
| b->blockno = blockno; | |
| b->flags = 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->flags & B_VALID) == 0) { | |
| iderw(b); | |
| } | |
| return b; | |
| } | |
| // Write b's contents to disk. Must be locked. | |
| void | |
| bwrite(struct buf *b) | |
| { | |
| if(!holdingsleep(&b->lock)) | |
| panic("bwrite"); | |
| b->flags |= B_DIRTY; | |
| iderw(b); | |
| } | |
| // Release a locked buffer. | |
| // Move to the head of the MRU list. | |
| void | |
| brelse(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); | |
| } | |
| //PAGEBREAK! | |
| // Blank page. | |
Xet Storage Details
- Size:
- 3.4 kB
- Xet hash:
- 0907e7000c13b2913fb228c62ecdace36ec53e30113f6cf6d3ae7d0320cb0fc7
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.