xv6, line by line
kernel/bio.c

kernel/bio.c

C · 153 lines · annotated 100% · kernel · upstream

About this file

The buffer cache: a small, fixed set of in-memory copies of disk blocks. Every disk access in the file system goes through it.

It does two jobs:

  1. Caching. Reading a block that is already in memory costs no disk access.
  2. Synchronization. There is at most one buffer per disk block, and each buffer has a sleep lock. So when two processes want the same block, they get the same buffer and take turns, and neither can see the other’s half-finished changes.

The interface is bread (get a locked buffer holding a block), bwrite (write it to disk; used only by the log), brelse (give it back), and bpin/bunpin (keep it in the cache while the log still needs it).

Below the cache sits the disk driver, kernel/virtio_disk.c. Above it sit kernel/log.c and kernel/fs.c. Note that the file system never calls bwrite: it modifies a buffer and calls log_write, and the log decides when the block reaches the disk.

Read before: kernel/buf.h. Read next: kernel/log.c.

1// Buffer cache.
2//
3// The buffer cache is a linked list of buf structures holding
4// cached copies of disk block contents. Caching disk blocks
5// in memory reduces the number of disk reads and also provides
6// a synchronization point for disk blocks used by multiple processes.
7//
8// Interface:
9// * To get a buffer for a particular disk block, call bread.
10// * After changing buffer data, call bwrite to write it to disk.
11// * When done with the buffer, call brelse.
12// * Do not use the buffer after calling brelse.
13// * Only one process at a time can use a buffer,
14// so do not keep them longer than necessary.
16#include "types.h"
17#include "param.h"
20#include "riscv.h"
21#include "defs.h"
22#include "fs.h"
23#include "buf.h"
25struct {
26 struct spinlock lock;
27 struct buf buf[NBUF];
29 // Linked list of all buffers, through prev/next.
30 // Sorted by how recently the buffer was used.
31 // head.next is most recent, head.prev is least.
32 struct buf head;
35void
36binit(void)
38 struct buf *b;
40 initlock(&bcache.lock, "bcache");
42 // Create linked list of buffers
45 for (b = bcache.buf; b < bcache.buf + NBUF; b++) {
48 initsleeplock(&b->lock, "buffer");
51 }
54// Look through buffer cache for block on device dev.
55// If not found, allocate a buffer.
56// In either case, return locked buffer.
57static struct buf *
60 struct buf *b;
64 // Is the block already cached?
65 for (b = bcache.head.next; b != &bcache.head; b = b->next) {
66 if (b->dev == dev && b->blockno == blockno) {
67 b->refcnt++;
70 return b;
71 }
72 }
74 // Not cached.
75 // Recycle the least recently used (LRU) unused buffer.
76 for (b = bcache.head.prev; b != &bcache.head; b = b->prev) {
77 if (b->refcnt == 0) {
78 b->dev = dev;
80 b->valid = 0;
81 b->refcnt = 1;
84 return b;
85 }
86 }
87 panic("bget: no buffers");
90// Return a locked buf with the contents of the indicated block.
91struct buf *
94 struct buf *b;
97 if (!b->valid) {
99 b->valid = 1;
100 }
101 return b;
104// Write b's contents to disk. Must be locked.
105// Only the log calls bwrite.
106void
107bwrite(struct buf *b)
110 panic("bwrite");
114// Release a locked buffer.
115// Move to the head of the most-recently-used list.
116void
117brelse(struct buf *b)
120 panic("brelse");
126 if (b->refcnt == 0) {
127 // no one is waiting for it.
134 }
139void
140bpin(struct buf *b)
147void
148bunpin(struct buf *b)