xv6——文件系统:磁盘的I/O操作和内存缓存机制
2022/7/11 5:20:15
本文主要是介绍xv6——文件系统:磁盘的I/O操作和内存缓存机制,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
目录
目录- 相关源码文件
- 架构图
- 数据结构
- 内存的缓存块
- 函数实现
- IDE磁盘的读写操作
- 磁盘串口读写操作函数
void idestart()
- 磁盘中断处理函数
void ideintr()
- 磁盘的读写处理函数
void iderw()
- 磁盘串口读写操作函数
- 内存缓存区操作函数
- Buffer块初始化函数
void binit()
- 查找指定的磁盘block块对应的buffer缓存块函数
struct buff* bget()
- 读取指定的磁盘的block块, 返回读取到的buffer块
bread()
- 写缓存块操作
bwrite()
- 释放指定的buffer块
void brelse()
- Buffer块初始化函数
- IDE磁盘的读写操作
作者:殷某人
更新时间: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); }
这篇关于xv6——文件系统:磁盘的I/O操作和内存缓存机制的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南