(done) 整理 xv6 文件系统 inode 层函数
url1: https://www.bilibili.com/video/BV138xuevELE?spm_id_from=333.788.videopod.sections&vd_source=7a1a0bc74158c6993c7355c5490fc600&p=31
url2: https://www.bilibili.com/video/BV138xuevELE?spm_id_from=333.788.videopod.sections&vd_source=7a1a0bc74158c6993c7355c5490fc600&p=32
url3: https://www.bilibili.com/video/BV138xuevELE?spm_id_from=333.788.videopod.sections&vd_source=7a1a0bc74158c6993c7355c5490fc600&p=33
dinode 结构体
nlink:这个 inode 拥有的硬链接数量
// On-disk inode structure
struct dinode {short type; // File typeshort major; // Major device number (T_DEVICE only)short minor; // Minor device number (T_DEVICE only)short nlink; // Number of links to inode in file systemuint size; // Size of file (bytes)uint addrs[NDIRECT+2]; // Data block addresses
};
itable 结构体
这其实是一个 inode cache,类似 buffer cache。
struct {struct spinlock lock;struct inode inode[NINODE];
} itable;
inode 结构体:每一个 inode 结构体都是磁盘上 inode 的缓存
dev, inum 表示这是哪个设备上的哪个 inode 的缓存
ref 表示 xv6 系统中一共有几个线程/任务正在使用这个 inode
lock 用来保护 inode 竞争
valid 表示数据是否有效
// in-memory copy of an inode
struct inode {uint dev; // Device numberuint inum; // Inode numberint ref; // Reference countstruct sleeplock lock; // protects everything below hereint valid; // inode has been read from disk?short type; // copy of disk inodeshort major;short minor;short nlink;uint size;uint addrs[NDIRECT+2];
};
根目录 inode 号为1
#define ROOTINO 1 // root i-number
iinit: 初始化 inode cache
fsinit: 接收设备号,初始化相关内容,这里会调用 readsb
readsb: 读取超级块
bzero: 往一个指定的块写入 0
balloc: 在位图中搜索空闲块,在位图标识空闲块为 “在使用”,随后返回这个块
bfree: 在位图中把一个 “在使用” 的块标识为空闲块
ialloc: 读取磁盘,获取一个未被使用的 inode,设置这个 inode 的类型,调用 iget
// Allocate an inode on device dev.
// Mark it as allocated by giving it type type.
// Returns an unlocked but allocated and referenced inode,
// or NULL if there is no free inode.
struct inode*
ialloc(uint dev, short type)
{int inum;struct buf *bp;struct dinode *dip;// inum 范围 1 ~ sb.ninodes, sb.ninodes 是超级块记录的 inode 数量for(inum = 1; inum < sb.ninodes; inum++){bp = bread(dev, IBLOCK(inum, sb));dip = (struct dinode*)bp->data + inum%IPB;// 找到空闲的 dinode 后,设置 type,然后释放,再调用 iget 获取 inode 结构体if(dip->type == 0){ // a free inodememset(dip, 0, sizeof(*dip));dip->type = type;log_write(bp); // mark it allocated on the diskbrelse(bp);return iget(dev, inum);}brelse(bp);}printf("ialloc: no inodes\n");return 0;
}
iget: 在 inode cache 中挑一个 inode,这里会提升 inode->ref
// 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?// 遍历整个 itable(inode cache),若找到 dev 和 inum 一致的 inode,直接返回这个 inode// 若没找到, empty 记录了一个空闲的 inode cacheempty = 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. (执行到这里说明 inode 不足)if(empty == 0)panic("iget: no inodes");// 该 inode 的 addrs 还没有被写入,无效ip = empty;ip->dev = dev;ip->inum = inum;ip->ref = 1;ip->valid = 0;release(&itable.lock);return ip;
}
ilock: 给 inode 加睡眠锁、从磁盘读取 inode
// 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);// 若 valid == False, 那么根据 inum 从磁盘读取 dinode,覆盖数据给 inumif(ip->valid == 0){bp = bread(ip->dev, IBLOCK(ip->inum, sb));dip = (struct dinode*)bp->data + ip->inum%IPB;ip->type = dip->type;ip->major = dip->major;ip->minor = dip->minor;ip->nlink = dip->nlink;ip->size = dip->size;memmove(ip->addrs, dip->addrs, sizeof(ip->addrs));brelse(bp);ip->valid = 1;if(ip->type == 0)panic("ilock: no type");}
}
iunlock: 解锁 inode
// Unlock the given inode.
void
iunlock(struct inode *ip)
{if(ip == 0 || !holdingsleep(&ip->lock) || ip->ref < 1)panic("iunlock");releasesleep(&ip->lock);
}
iupdate: 把内存 inode 写回磁盘 (写到相应的 buffer 里)
(事实上,iupdate 会在很多地方被调用。你可能觉得这里有 redudant work,但请记住,我们使用的是磁盘日志系统,iupdate 在更新日志,只有最终 end_op() 时才会把日志上的内容更新到磁盘数据块里)
// Copy a modified in-memory inode to disk.
// Must be called after every change to an ip->xxx field
// that lives on disk.
// Caller must hold ip->lock.
void
iupdate(struct inode *ip)
{struct buf *bp;struct dinode *dip;bp = bread(ip->dev, IBLOCK(ip->inum, sb));dip = (struct dinode*)bp->data + ip->inum%IPB;dip->type = ip->type;dip->major = ip->major;dip->minor = ip->minor;dip->nlink = ip->nlink;dip->size = ip->size;memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));log_write(bp);brelse(bp);
}
idup: 让 inode->ref +1
// Increment reference count for ip.
// Returns ip to enable ip = idup(ip1) idiom.
struct inode*
idup(struct inode *ip)
{acquire(&itable.lock);ip->ref++;release(&itable.lock);return ip;
}
iput: 让 inode->ref -1。若-1后 ref == 0 且 nlink == 0,清除这个内存 inode,同时调用 iupdate 同步到磁盘 inode 上。
// Drop a reference to an in-memory inode.
// If that was the last reference, the inode table entry can
// be recycled.
// If that was the last reference and the inode has no links
// to it, free the inode (and its content) on disk.
// All calls to iput() must be inside a transaction in
// case it has to free the inode.
void
iput(struct inode *ip)
{acquire(&itable.lock);if(ip->ref == 1 && ip->valid && ip->nlink == 0){// inode has no links and no other references: truncate and free.// ip->ref == 1 means no other process can have ip locked,// so this acquiresleep() won't block (or deadlock).acquiresleep(&ip->lock);release(&itable.lock);itrunc(ip);ip->type = 0;iupdate(ip);ip->valid = 0;releasesleep(&ip->lock);acquire(&itable.lock);}ip->ref--;release(&itable.lock);
}
iunlockput: 先调用 iunlock, 在调用 iput
itrunc: 清除内存 inode 的数据块,设置 inode->size = 0,最后调用 iupdate 同步到磁盘 inode 上
stati: 拷贝 inode 的数据到一个指定地点,这让用户态程序能够了解一个文件的 inode 信息
readi: 从 inode 代表的文件中读取数据
writei:往 inode 代表的文件写入数据
dirlookup: 在一个目录下根据文件名查找指定文件,若找到,调用 iget 返回文件 inode
dirlink: 用来往目录添加文件
skipelem: 传入一个路径字符串,获取第一个目录,比如传入 “a/b/c”,这个函数会返回 a
namei: 根据路径名返回 inode
nameiparent: 返回路径所代表的最终文件的所在的目录的 inode。比如传入路径 “a/b/c”,这个函数会返回 b 的 inode
namex: 做 namei 和 nameiparent 所做的事情
TODO: here