ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

xv6——文件系统:磁盘的I/O操作和内存缓存机制

2022-07-10 23:35:47  阅读:700  来源: 互联网

标签:缓存 struct buffer 文件系统 xv6 磁盘 bcache buf


目录

目录


作者:殷某人
更新时间:2022/07/03

相关源码文件

buf.h
bio.c
ide.c

架构图

数据结构

内存的缓存块

缓存块用于缓存磁盘上的一个block, 大小为512字节, 在内存中,缓存块采用数组+双向链表的数据结构,使用链表的目的是进行LRU优化, 链表的顺序按照最近使用顺序进行排列, 提高复用率,减少IO操作。
每一个buffer块都有自己的一把锁,一个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字节的数据缓存区
};
  • buffer Table的数据结构定义如下:
struct {
  struct spinlock lock;   // 锁
  struct buf buf[NBUF];   // buffer块数组,一大块连续的buffer
  struct buf head;        // 它可以看出一个哨兵,目的方便双向链表的操作
                          // head.next 是第一个buffer块, 它是最近使用过的!
} bcache;

函数实现

IDE磁盘的读写操作

磁盘串口读写操作函数 void idestart()

  • 计算要主读取的sector块数,通常512字节对应一个sector.
  • 写操作:CPU只负责写到磁盘的串口寄存器, 磁盘自己会执行真正的写到磁盘的操作.完成之后,会通过中断告诉CPU
  • 读操作:CPU通知磁盘读取512字节内容到磁盘寄存器,完成之后,通过中断告诉CPU,CPU之后会把内容复制到内存中
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()

  • 在收到中断处理函数时, 如果是磁盘读取工作完成,会负责把内容从寄存器读取到内存缓存区。
  • 刷新标志位,清掉dirdy, 置位valid.

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()

  • 把需要同步的buffer块添加到操作队列中
  • 如果队列内只有它自己,说明队列任务未启动, 启动磁盘同步。
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);
}

内存缓存区操作函数

Buffer块初始化函数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;
  }
}

查找指定的磁盘block块对应的buffer缓存块函数struct buff* bget()

  • 如果该bock块已经缓存过,直接返回缓存的buffer块
  • 如果没有缓存过,查找一块空闲的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");
}

读取指定的磁盘的block块, 返回读取到的buffer块bread()

  • 如果该bock块已经缓存过,直接返回缓存的buffer块,它当前数据是最新的。
  • 如果没有缓存过,需要把磁盘上的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;
}

写缓存块操作 bwrite()

  • 写之前,该buffer块一定是已经加锁的。
  • 写完成之后,会执行缓存区到磁盘的写操作。
void
bwrite(struct buf *b)
{
  if(!holdingsleep(&b->lock))
    panic("bwrite");
  b->flags |= B_DIRTY;
  iderw(b);
}

释放指定的buffer块 void brelse()

  • 释放锁, 减少引用次数
  • 若该buffer没有进程使用时,把它移动到链接头部,LRU。
// 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);
}

标签:缓存,struct,buffer,文件系统,xv6,磁盘,bcache,buf
来源: https://www.cnblogs.com/yinheyi/p/16464413.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有