os-xv6分析-文件系统-bio.c 代码分析
// 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.
//缓冲区缓存是保存磁盘块内容缓存副本的buf结构的链接列表。在内存中缓存//磁盘块可以减少磁盘读取的次数,并为多个进程使用的磁盘块提供一个同步点。
// 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.
// 导包
#include "types.h"
#include "defs.h"
#include "param.h"
#include "spinlock.h"
#include "sleeplock.h"
#include "fs.h"
#include "buf.h"
// 块缓存的管理数据结构
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;
//初始化函数,用于创建bcache互斥锁和将块缓存buf构成一个LRU双向链表
void
binit(void)
{
struct buf *b;
initlock(&bcache.lock, "bcache"); //初始化锁
//PAGEBREAK!
// Create linked list of buffers // 将全部buf构成双向链表
bcache.head.prev = &bcache.head;
bcache.head.next = &bcache.head;
// 遍历buf数组,将每一个用头插法插入到链表中,head是头节点
// 之后都是通过访问链表,而不是数组buf来访问buffer
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.
//bget应该是寻找磁盘上一个块对应的buffer,首先遍历链表,看该块是否已经
//缓存,如果是,那么直接返回。如果没有,那么从双端链表中找一个LRU的//buffer(empty slot),回收利用。
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 //B_Busy 和 B_DIRTY
// 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; // 返回这个缓存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); // 调用bget函数,找到或分配一个块缓存
if((b->flags & B_VALID) == 0) { // 判断valid是否有数据
iderw(b); //通过IDE设备驱动程序iderw()读入
}
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); //通过IDE设备驱动程序iderw()写入
}
// Release a locked buffer.
// Move to the head of the MRU list.
void
brelse(struct buf *b) //用完后释放,并挂到LRU链表头
{
if(!holdingsleep(&b->lock)) //判读是否正在使用
panic("brelse");
releasesleep(&b->lock); //释放睡眠锁
acquire(&bcache.lock); // 获得锁
b->refcnt--;
// 将b从原来的位置取下,放在链表开头
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.