目录
目录void idestart()
void ideintr()
void iderw()
void binit()
struct buff* bget()
bread()
bwrite()
void brelse()
作者:殷某人
更新时间:2022/07/03
buf.h bio.c ide.c
缓存块用于缓存磁盘上的一个block, 大小为512字节, 在内存中,缓存块采用数组+双向链表的数据结构,使用链表的目的是进行LRU优化, 链表的顺序按照最近使用顺序进行排列, 提高复用率,减少IO操作。
每一个buffer块都有自己的一把锁,一个buffer块同一时间只能有一个进程拥有。
struct buf { int flags; // 标志位,b_valid和b_dirty. uint dev; // 缓存块对应的磁盘设备号 uint blockno; // 缓存块对应的block块号 struct sleeplock lock; // 睡眠锁, 保证一个buffer同一时间只可能被一个进程拥有 uint refcnt; // 引用次数 struct buf *prev; // LRU双向链表 struct buf *next; // LRU双向链表 struct buf *qnext; // 当Buffer块需要与磁盘间进行同步时,Buffer块之间组成的单向同步队列 uchar data[BSIZE]; // 512字节的数据缓存区 };
struct { struct spinlock lock; // 锁 struct buf buf[NBUF]; // buffer块数组,一大块连续的buffer struct buf head; // 它可以看出一个哨兵,目的方便双向链表的操作 // head.next 是第一个buffer块, 它是最近使用过的! } bcache;
void idestart()
static void idestart(struct buf *b) { if(b == 0) panic("idestart"); if(b->blockno >= FSSIZE) panic("incorrect blockno"); int sector_per_block = BSIZE/SECTOR_SIZE; int sector = b->blockno * sector_per_block; int read_cmd = (sector_per_block == 1) ? IDE_CMD_READ : IDE_CMD_RDMUL; int write_cmd = (sector_per_block == 1) ? IDE_CMD_WRITE : IDE_CMD_WRMUL; if (sector_per_block > 7) panic("idestart"); idewait(0); outb(0x3f6, 0); // generate interrupt outb(0x1f2, sector_per_block); // number of sectors outb(0x1f3, sector & 0xff); outb(0x1f4, (sector >> 8) & 0xff); outb(0x1f5, (sector >> 16) & 0xff); outb(0x1f6, 0xe0 | ((b->dev&1)<<4) | ((sector>>24)&0x0f)); if(b->flags & B_DIRTY){ outb(0x1f7, write_cmd); outsl(0x1f0, b->data, BSIZE/4); } else { outb(0x1f7, read_cmd); } }
void ideintr()
void ideintr(void) { struct buf *b; // First queued buffer is the active request. acquire(&idelock); if((b = idequeue) == 0){ release(&idelock); return; } idequeue = b->qnext; // Read data if needed. if(!(b->flags & B_DIRTY) && idewait(1) >= 0) insl(0x1f0, b->data, BSIZE/4); // Wake process waiting for this buf. b->flags |= B_VALID; b->flags &= ~B_DIRTY; wakeup(b); // Start disk on next buf in queue. if(idequeue != 0) idestart(idequeue); release(&idelock); }
void iderw()
void iderw(struct buf *b) { struct buf **pp; if(!holdingsleep(&b->lock)) panic("iderw: buf not locked"); if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) panic("iderw: nothing to do"); if(b->dev != 0 && !havedisk1) panic("iderw: ide disk 1 not present"); acquire(&idelock); //DOC:acquire-lock // Append b to idequeue. b->qnext = 0; for(pp=&idequeue; *pp; pp=&(*pp)->qnext) //DOC:insert-queue ; *pp = b; // Start disk if necessary. if(idequeue == b) idestart(b); // Wait for request to finish. while((b->flags & (B_VALID|B_DIRTY)) != B_VALID){ sleep(b, &idelock); } release(&idelock); }
void binit()
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; } }
struct buff* bget()
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"); }
bread()
struct buf* bread(uint dev, uint blockno) { struct buf *b; b = bget(dev, blockno); if((b->flags & B_VALID) == 0) { iderw(b); } return b; }
bwrite()
void bwrite(struct buf *b) { if(!holdingsleep(&b->lock)) panic("bwrite"); b->flags |= B_DIRTY; iderw(b); }
void brelse()
// 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); }