文章目录
1. Buffer cache layer
balloc
要分配一个新的block,就需要根据bitmap了解block的free/allocated情况。因此,可以遍历data block,对于每一个data block查看是否是free,如果是就直接返回这个block就可以了。
在Xv6中,每个data block都去查找一次bitmap block这样效率其实很低,因为data block只是bitmap block的one bit,这时候如果正好把整个bitmap block的所有bits都检查一遍,效率很提升很多。
下面代码中,外层循环b表示一个data block,遍历每一个data block(从下标0开始),通过BBLOCK(b, sb)
计算出data block b是bitmap block的第几个block。由于我们每次把一整个bitmap block都检查完,所以相当于一次检查了BPB(8 * 1024)
个data block,所以外层循环每次b
递增BPB
。
BBLOCK
的计算原理,之前在学校操作系统课上算的很溜,写代码这里有点愣了,正好复习一下。
如何计算一个No. B data block在Bitmap中是第几个block?
第一个Bitmap block起始位置的block + B
作为bitmap,一个block的大小是1024bytes,可以存储的bit有10248个block。因此 B / ( 1024 ∗ 8 ) B / (1024*8) B/(1024∗8)就是应该在bitmap的哪一个block。
一个bitmap block可以有10248个bits就可以代表这么多的data block。1024*8在Xv6代码中是BPB = (BSIZE * 8)
读取到bp后,内层循环bi表示一个bitmap bit,每次递增1。
由于bread
返回的是有lock的buffer,所以这里也会防止race condition导致的死锁。最后通过brelse
释放锁。
// Allocate a zeroed disk block.
static uint
balloc(uint dev)
{
int b, bi, m;
struct buf *bp;
bp = 0;
for(b = 0; b < sb.size; b += BPB){
bp = bread(dev, BBLOCK(b, sb));
for(bi = 0; bi < BPB && b + bi < sb.size; bi++){
m = 1 << (bi % 8);
if((bp->data[bi/8] & m) == 0){
// Is block free?
bp->data[bi/8] |= m; // Mark block in use.
log_write(bp);
brelse(bp);
bzero(dev, b + bi);
return b + bi;
}
}
brelse(bp);
}
panic("balloc: out of blocks");
}
Bfree
有了理解balloc的基础上,理解Bfree就更容易了。
- 读取对应bitmap block到buffer
bp
中 - 找到data block
b
在bitmap block中对应的bitbi
。 - 使用mask方法把把
bi
对应的位置设置为0(free)。 - 调用
log_write(bp)
。 - 释放bp
// Free a disk block.
static void
bfree(int dev, uint b)
{
struct buf *bp;
int bi, m;
bp = bread(dev, BBLOCK(b, sb));
bi = b % BPB;
m = 1 << (bi % 8);
if((bp->data[bi/8] & m) == 0)
panic("freeing free block");
bp->data[bi/8] &= ~m;
log_write(bp);
brelse(bp);
}
2. Inode Layer
Ialloc&Iget
Ialloc的作用是分配一个inode,inode在disk中存储的方式也是block,一个block的大小为1024bytes,struct dinode
(disk中的inode形式)为8字节,所以一个block能存放128个inode的信息。
// On-disk inode structure
struct dinode {
short type; // File type
short major; // Major device number (T_DEVICE only)
short minor; // Minor device number (T_DEVICE only)
short nlink; // Number of links to inode in file system
uint size; // Size of file (bytes)
uint addrs[NDIRECT+1]; // Data block addresses
};
要分配一个inode,需要遍历disk的inode所有节点,查看是否有一个inode是free状态。
- 首先从disk中读取对应inum所在的block,经过IBLOCK进行计算inum在disk中的block位置
- 在kernel获取到bp指针后,从bp指针的
data
中获取在一个block里面所有struct dinode
,通过IPB计算出当前inum对应的inode应该在哪个位置,dip
指针指向该位置。 - 如果发现
dip->type == 0
说明这是一个free inode,在返回这个inode之前,必须要将disk的struct dinode
转换成内存中的struct inode
这两个结构是不一样的,调用iget(uint dev, uint inum)
方法,会寻找这个inode是否已经在内存的有cache了,如果没有cache当前inode,直接寻找一个free的inode cache,写入inode信息到cache的inode,包含dev,inum,ref。但是不要lock,不要从disk读取,这是iget特殊的地方。这里有一个注意的地方就是ip->valid = 0
而不是1,这里在下面的ilock
会用到这个valid来从硬盘读取inode的信息。
// Inodes per block.
#define IPB (BSIZE / sizeof(struct dinode))
// Block containing inode i
#define IBLOCK(i, sb) ((i) / IPB + sb.inodestart)Frans Kaashoek, 6 years ago: • Pick up where i left off in april:
// Allocate an inode on device dev.
// Mark it as allocated by giving it type type.
// Returns an unlocked but allocated and referenced inode.
struct inode*
ialloc(uint dev, short type)
{
int inum;
struct buf *bp;
struct dinode *dip;
for(inum = 1; inum < sb.ninodes; inum++){
bp = bread(dev, IBLOCK(inum, sb));
dip = (struct dinode*)bp->data + inum%IPB;
if(dip->type == 0){
// a free inode
memset(dip, 0, sizeof(*dip));
dip->type = type;
log_write(bp); // mark it allocated on the disk
brelse(bp);
return iget(dev, inum);
}
brelse(bp);
}
panic("ialloc: no inodes");
}
// Find the inode with number inum on device dev
// and return the in-memory copy. Does not lock
// the inode and does not read it from disk.
static struct inode*
iget(uint dev, uint inum)
{
struct inode *ip, *empty;
acquire(&itable.lock);
// Is the inode already in the table?
empty = 0;
for(ip = &itable.inode[0]; ip < &itable.inode[NINODE]; ip++){
if(ip->ref > 0 && ip->dev == dev && ip->inum == inum){
ip->ref++;
release(&itable.lock);
return ip;
}
if(empty == 0 && ip->ref == 0) // Remember empty slot.
empty = ip;
}
// Recycle an inode entry.
if(empty == 0)
panic("iget: no inodes");
ip = empty;
ip->dev = dev;
ip->inum = inum;
ip->ref = 1;
ip->valid = 0;
release(&itable.lock);
return ip;
}
void ilock(struct inode *ip)
- 想要读取或者修改inode的任何数据,必须先调用
ilock
方法,确保只有一个进程可以访问一个in-memory inode。 - ilock使用的是sleep lock。
- 如果
inode->valid = 0
,就需要从disk中读取信息。正好符合iget将inode->valid = 0
match上。 - 解锁需要使用
iunlock(struct inode *ip)
// Lock the given inode.
// Reads the inode from disk if necessary.
void
ilock(struct inode *ip)
{
struct buf *bp;
struct dinode *dip;
if(ip == 0 || ip->ref < 1)
panic("ilock");
acquiresleep(&ip->lock);
if(ip->valid == 0){
bp = bread(ip->dev, IBLOCK(ip->inum, sb));
dip = (struct dinode*)bp<