阅读材料
- Xv6代码:buf.h、bio.c
- Xv6教材第8章 8.1-8.3节
缓存层作用
缓存层,也就是教材中的Buffer cache layer,是Xv6整个文件系统中最低一层,该层直接读、写磁盘的数据。该层在整个文件系统的作用如下:
- 同步对磁盘块的访问,以确保内存中只有一个块的副本,并且一次只有一个内核线程使用该副本
- 为了减少从磁盘读块,缓存常用的块
Buffer cache数据结构
缓存层用一个循环双向链表实现的LRU来管理struct buf,总共有NBUF(定义在param.h)个struct buf
其中,头节点是dummy节点,并不真实存储数据
struct
{struct spinlock lock;struct buf buf[NBUF];struct buf head;
} bcache;
struct buf的结构如下
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];
};
缓存层接口
binit()函数
该函数是Xv6内核初始化时,文件系统模块第一个调用的函数,是整个文件系统的根基
该函数首先初始化bcache的自选锁,然后遍历所有buf,初始化每个buf的睡眠锁,并插入到链表中
void binit(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;}
}
bread()函数
该函数返回指定块号的副本,通过调用bget函数得到该块。如果该块还未被缓存到内存,则调用virtio_disk_rw函数从磁盘中读取。virtio_disk_rw函数(定义在virtio_disk.c)是Xv6虚拟磁盘驱动提供的唯一接口,用来对虚拟磁盘进行读/写操作
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;
}
bget函数用于遍历整个链表,寻找合适的缓存块
注意:这里其实依赖blockno和refcnt被隐式初始化为0的事实,不然会有bug
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 the least recently used (LRU) 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");
}
bwrite()函数
该函数首先检查是否持有睡眠锁,然后通过virtio_disk_rw函数写入虚拟磁盘中
void bwrite(struct buf *b)
{if (!holdingsleep(&b->lock))panic("bwrite");virtio_disk_rw(b, 1);
}
bpin()函数
增加该buffer块的引用计数
void bpin(struct buf *b)
{acquire(&bcache.lock);b->refcnt++;release(&bcache.lock);
}
bunpin()函数
减少该buffer块的引用计数
void bunpin(struct buf *b)
{acquire(&bcache.lock);b->refcnt--;release(&bcache.lock);
}
参考资料
The xv6 Kernel-28 Disk Buffer Cache_哔哩哔哩_bilibili