参考xv6源码分析--文件系统。
一、Large files
1 目的
目前xv6包含12个直接索引和1个二级索引,共索引12KB 256KB。增加xv6文件的索引范围,使得能够索引256*256KB 256KB 11KB,牺牲一个直接索引,将其转变为三级索引。
2 问题分析
mkfs程序会创建xv6文件系统磁盘镜像并决定文件系统有多少blocks,这个大小受kernel/param.h中的FSSIZE控制。
访问索引的场景:
- 读写block时需要根据文件偏移量从索引块中获取磁盘block编号,bmap函数;
- 清空文件时需要遍历索引块并释放掉磁盘空间,itrunc函数。
3 代码实现
读写block时调用bmap获取磁盘块地址,
- 如果在0, 10,那么就读取直接索引;
- 如果在11, 266,那么就先读取single-indirect索引块,然后读取指定索引项;
- 如果在267, 65803,那么就读取两级索引块,再根据偏移量获取索引项。
//返回该偏移量的block地址
static uint
bmap(struct inode *ip, uint bn)
{
uint addr, *a;
struct buf *bp;
//从直接索引读取blockno
if(bn < NDIRECT){
if((addr = ip->addrs[bn]) == 0)
ip->addrs[bn] = addr = balloc(ip->dev);
return addr;
}
bn -= NDIRECT;
//从二级索引中读取blockno
if(bn < NINDIRECT){
// Load indirect block, allocating if necessary.
if((addr = ip->addrs[NDIRECT]) == 0)
ip->addrs[NDIRECT] = addr = balloc(ip->dev);
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if((addr = a[bn]) == 0){
a[bn] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
return addr;
}
//访问三级索引
bn-=NINDIRECT;
if(bn < NDBINDIRECT){
// Load indirect block, allocating if necessary.
//分配顶级目录
if((addr = ip->addrs[NDIRECT 1]) == 0)
ip->addrs[NDIRECT 1] = addr = balloc(ip->dev);
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
//分配二级目录
int mediumindex=bn/NINDIRECT;
if((addr = a[mediumindex]) == 0){
a[mediumindex] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
//分配一级目录
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
int bottomindex=bn%NINDIRECT;
if((addr = a[bottomindex]) == 0){
a[bottomindex] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
// printf("三级索引,bn[%d],addr[%d]n",bn,addr);
return addr;
}
panic("bmap: out of range");
}
释放文件空间时也需要访问索引并释放掉所有block,依次释放直接索引指向的block,single-indirect指向的block以及double-indirect指向的block,并将索引块也释放掉,设置size=0。
代码语言:c复制// Truncate inode (discard contents).
// Caller must hold ip->lock.
void
itrunc(struct inode *ip)
{
int i, j;
struct buf *bp;
uint *a;
//释放直接索引的block
for(i = 0; i < NDIRECT; i ){
if(ip->addrs[i]){
bfree(ip->dev, ip->addrs[i]);
ip->addrs[i] = 0;
}
}
//释放间接索引的block
if(ip->addrs[NDIRECT]){
bp = bread(ip->dev, ip->addrs[NDIRECT]);
a = (uint*)bp->data;
for(j = 0; j < NINDIRECT; j ){
if(a[j])
bfree(ip->dev, a[j]);
}
brelse(bp);
bfree(ip->dev, ip->addrs[NDIRECT]);
ip->addrs[NDIRECT] = 0;
}
//释放二级间接索引
//释放间接索引的block
if(ip->addrs[NDIRECT 1]){
bp = bread(ip->dev, ip->addrs[NDIRECT 1]);
a = (uint*)bp->data;
struct buf* bbp;
uint *aa;
for(j = 0; j < NINDIRECT; j ){
if(0==a[j])
continue;
bbp=bread(ip->dev,a[j]);
aa=(uint*)bbp->data;
for(int i=0;i< NINDIRECT;i ){
if(aa[i])
bfree(ip->dev,aa[i]);
}
brelse(bbp);
}
brelse(bp);
bfree(ip->dev, ip->addrs[NDIRECT 1]);
ip->addrs[NDIRECT 1] = 0;
}
ip->size = 0;
//更新磁盘上inode
iupdate(ip);
}
二、Symbolic links
1 目的
本任务是实现symlink(char *target, char *path)
系统调用,给xv6添加符号链接。符号链接是一个特殊文件,保存了被链接文件的path。硬链接是创建新的目录项指向已存在文件的inode,但是只能指向磁盘上文件,而符号链接更加灵活,能指向设备文件。
2 问题分析
符号链接文件和普通文件的读写、打开、创建不一样,如下:
- 符号链接是从文件内容读取path,然后根据dirlookup查找目标文件的inode,与目录、普通文件不一样,所以需要定义新的文件类型
T_SYMLINK
; - 添加symlink系统调用,它会调用create创建符号链接文件,这个和普通文件的创建有所不同;
- 添加一个flag O_NOFOLLOW,使得能够打开符号文件,如果被链接的文件也是一个符号链接文件,那就需要递归的查找直到遇到普通文件为止,如果链接关系是一个环,则需要error,可以根据递归深度来决定;
- 其他的系统调用打开符号链接文件就是操作符号链接文件本身,不需要查找被链接的文件。
3 代码实现
首先增加系统调用symlink;
代码语言:c复制//syscall.h
#define SYS_symlink 22
//usys.pl
entry("symlink");
//syscall.c
[SYS_symlink] sys_symlink,
//stat.h
#define T_SYMLINK 4 //symbolic link
然后实现sys_symlink(),如下:
- 读取target字符串和path地址;
- 查找path是否存在,如果不存在就创建该符号文件;
- 向该文件写入target。
uint64
sys_symlink(void){
char target[MAXPATH],path[MAXPATH];
if(argstr(0,target,MAXPATH)<0)
return -1;
if(argstr(1,path,MAXPATH)<0)
return -1;
struct inode *ip;
struct file *f;
int fd;
begin_op();
//查找该path的文件inode是否存在
if((ip = namei(path)) == 0){
ip=create(target,T_SYMLINK,0,0);
if(ip==0){
end_op();
return -1;
}
}else{
ilock(ip);
}
if(writei(ip,0,target,ip->size,MAXPATH)!=MAXPATH){
panic("panic: symlink");
}
iunlockput(ip);
end_op();
return 0;
}
打开符号链接文件时要区分,以普通文件形式打开来读写符号链接文件,还是以O_NOFOLLOW=0模式打开读取被链接的普通文件。如果是访问被链接的文件,那么就递归的查找inode,通过readi从inode中读取数据symbolic path,然后读取symbolicpath inode并判断是不是T_SYMLINK,如果是就继续递归。为了防止出现环,最多递归10次,超过就认为有环。
代码语言:c复制if(ip->type==T_SYMLINK && (omode & O_NOFOLLOW)==0){
int count=0;
char symlinkpath[MAXPATH];
while(1){
if(count>10){
iunlockput(ip);
end_op();
return -1;
}
if(readi(ip,0,(uint64)symlinkpath,ip->size-MAXPATH,MAXPATH)!=MAXPATH){
panic("panic: symlink");
}
iunlockput(ip);
if((ip=namei(symlinkpath))==0){
end_op();
return -1;
}
ilock(ip);
if(ip->type!=T_SYMLINK){
break;
}
count ;
}
}