Lab: file system
Large files
实验要求:实现磁盘的二级映射,将磁盘空间由268 blocks扩大为65803 blocks,约为原来的256倍
实现思路:将原来的12个直接映射+1个一级间接映射改为11个直接映射+1个一级间接映射+一个二级映射。
//fs.h
#define NDIRECT 11 //注意在进入二级映射之前,记得减掉一个NINDIRECT,保证获取的中间地址号不会多1。
#define NINDIRECT (BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + NINDIRECT + (NINDIRECT*NINDIRECT))
// 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+2]; // Data block addresses
};
//file.h
// in-memory copy of an inode
struct inode {
uint dev; // Device number
uint inum; // Inode number
int ref; // Reference count
struct sleeplock lock; // protects everything below here
int valid; // inode has been read from disk?
short type; // copy of disk inode
short major;
short minor;
short nlink;
uint size;
uint addrs[NDIRECT+2];
};
//fs.c
static uint
bmap(struct inode *ip, uint bn)
{
uint addr, *a;
struct buf *bp;
// 直接块 block[0-10]
if(bn < NDIRECT){
if((addr = ip->addrs[bn]) == 0)
ip->addrs[bn] = addr = balloc(ip->dev);
return addr;
}
bn -= NDIRECT;
//s_indirect block[11] [0-255]
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;
}
//d_indirect block[12]
bn -= NINDIRECT;
if(bn < NINDIRECT*NINDIRECT)
{
int mid_addr_num = bn / NINDIRECT; //间接块
bn = bn % NINDIRECT;
// 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;
if ((addr = a[mid_addr_num]) == 0)
{
a[mid_addr_num] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
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;
}
panic("bmap: out of range");
}
// Truncate inode (discard contents).
// Caller must hold ip->lock.
void
itrunc(struct inode *ip)
{
int i, j, k;
struct buf *bp;
uint *a, *botton_a;
// 11 direct block[0 ~ 10] [0 ~ 10]
for(i = 0; i < NDIRECT; i++){
if(ip->addrs[i]){
bfree(ip->dev, ip->addrs[i]);
ip->addrs[i] = 0;
}
}
// 1 s_direct block[11] => [0 ~ 255]
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;
}
// 1 d_direct block[12] => 256 * [0 ~ 255]
if(ip->addrs[NDIRECT+1])
{
bp = bread(ip->dev, ip->addrs[NDIRECT+1]);
a = (uint *)bp->data;
brelse(bp);
for(j=0; j<NINDIRECT; ++j)
{
if(a[j])
{
bp = bread(ip->dev, a[j]);
botton_a = (uint *)bp->data;
brelse(bp);
for(k=0; k<NINDIRECT; ++k)
{
if(botton_a[k])
bfree(ip->dev, botton_a[k]);
}
}
}
bfree(ip->dev, ip->addrs[NDIRECT + 1]);
ip->addrs[NDIRECT + 1] = 0;
}
ip->size = 0;
iupdate(ip);
}
Symbolic links
实验要求:实现软连接(symboil/soft link)
实现思路:实现syccall: sys_symlink,将目标文件inode指针存储到路径文件的inode结构中,之后即可通过路径文件来访问目标文件
具体实现过程中,需要注意路径文件本身可能存在也可能不存在,所以需要检查,根据情况进行创建。这需要处理两个情况,即如果路径文件本身不存在,创建出来后是带锁的,那么自然不需要上锁;但如果路径文件本身就存在,那么它是没有锁的,那么我们就需要使用if(!holdingsleep(&ip->lock))进行检查。
同时,软连接到一个不存在的目标文件是被允许的,也就是说目标文件和路径文件一样,可能存在也可能不存在,所以需要检查,根据情况进行创建。这就会导致一个情况,就是如果带着O_CREATE参数去open()目标文件时,目标文件可能已经在symlink()被创建,如果再创建就是重复创建了,解决方法是进行检查,如果是T_SYMLINK,则不用新创建,改下参数即可;如果不是,则需要将其释放掉,然后再去创建。
在open()函数中循环查找时记得解锁,再上锁,保证原子性,查找结束后,需要检查一下目标文件是否真的存在,存在的情况是nlink大于0。
无论是目标文件还是路径文件,只要是T_SYMLINK类型,当被unlink()时,就需要执行iput(),将其引用减一,让其有机会被释放掉,否则T_SYMLINK类型的inode就会遗留在cache,无法释放,数量一多,就会导致cache中没有空闲的inode可以存储了。
首先是syccall: sys_symlink的注册流程,
//fcnt.h
#define O_RDONLY 0x000
#define O_WRONLY 0x001
#define O_RDWR 0x002
#define O_CREATE 0x200
#define O_TRUNC 0x400
#define O_NOFOLLOW 0x800
//file.h
// in-memory copy of an inode
struct inode {
uint dev; // Device number
uint inum; // Inode number
int ref; // Reference count
struct sleeplock lock; // protects everything below here
int valid; // inode has been read from disk?
short type; // copy of disk inode
short major;
short minor;
short nlink;
uint size;
uint addrs[NDIRECT+2];
struct inode *sym_link; //add
};
//stat.h
#define T_DIR 1 // Directory
#define T_FILE 2 // File
#define T_DEVICE 3 // Device
#define T_SYMLINK 4 // symbolic link.
然后是实现sys_symlink
uint64 sys_symlink(void)
{
char target[MAXPATH], path[MAXPATH];
struct inode *ip, *ip_target;
//获得参数target path
if (argstr(0, target, MAXPATH) < 0 || argstr(1, path, MAXPATH) < 0)
{
return -1;
}
begin_op();
// create ip_target if not exist
if((ip_target = namei(target)) == 0)
{
if((ip_target = create(target, T_SYMLINK, 0, 0)) == 0)
{
end_op();
return -1;
}
}
// create ip if not exist
if((ip = namei(path)) == 0)
{
if ((ip = create(path, T_SYMLINK, 0, 0)) == 0)
{
end_op();
return -1;
}
}
// 在本实验中不必处理目录的符号链接。
if (ip_target)
{
if (ip_target->type == T_DIR)
{
end_op();
return -1;
}
}
if (!holdingsleep(&ip->lock))
{
ilock(ip);
}
ip->sym_link = ip_target; //在inode中存储指向的文件路径
iupdate(ip);
iunlock(ip);
end_op();
return 0;
}
open
uint64
sys_open(void)
{
char path[MAXPATH];
int fd, omode;
struct file *f;
struct inode *ip;
int n;
if((n = argstr(0, path, MAXPATH)) < 0 || argint(1, &omode) < 0)
return -1;
begin_op();
if(omode & O_CREATE)
{
ip = namei(path);
if(ip) //存在
{
// if it is T_SYMLINK, we just change it type
if (ip->type == T_SYMLINK)
{
ip->type = T_FILE;
ip->major = ip->minor = 0;
}
else{
//释放ip,并新建T_FILE类型的
iput(ip);
ip = create(path, T_FILE, 0, 0);
if(ip == 0)
{
end_op();
return -1;
}
}
}
else{ //不存在新建
ip = create(path, T_FILE, 0, 0);
if(ip == 0)
{
end_op();
return -1;
}
}
}
else{
if((ip = namei(path)) == 0)
{
end_op();
return -1;
}
ilock(ip);
if(ip->type == T_DIR && omode != O_RDONLY)
{
iunlockput(ip);
end_op();
return -1;
}
}
//检查是否是符号链接
if(ip->type == T_SYMLINK)
{
if (!(omode & O_NOFOLLOW))
{
int threshold = 10;
//循环搜索, 阈值10
for(int i=0; i<threshold; ++i)
{
iunlockput(ip);
ip = ip->sym_link;
ilock(ip);
if(ip->type != T_SYMLINK)
break;
}
// 检查一下目标文件是否真的存在
if(ip->nlink == 0)
{
iunlockput(ip);
end_op();
return -1;
}
}
}
if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
iunlockput(ip);
end_op();
return -1;
}
if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
if(f)
fileclose(f);
iunlockput(ip);
end_op();
return -1;
}
if(ip->type == T_DEVICE){
f->type = FD_DEVICE;
f->major = ip->major;
} else {
f->type = FD_INODE;
f->off = 0;
}
f->ip = ip;
f->readable = !(omode & O_WRONLY);
f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
if((omode & O_TRUNC) && ip->type == T_FILE){
itrunc(ip);
}
iunlock(ip);
end_op();
return fd;
}
unlink
uint64
sys_unlink(void)
{
struct inode *ip, *dp;
struct dirent de;
char name[DIRSIZ], path[MAXPATH];
uint off;
if(argstr(0, path, MAXPATH) < 0)
return -1;
begin_op();
if((dp = nameiparent(path, name)) == 0){
end_op();
return -1;
}
ilock(dp);
// Cannot unlink "." or "..".
if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0)
goto bad;
if((ip = dirlookup(dp, name, &off)) == 0)
goto bad;
ilock(ip);
if(ip->nlink < 1)
panic("unlink: nlink < 1");
if(ip->type == T_DIR && !isdirempty(ip)){
iunlockput(ip);
goto bad;
}
memset(&de, 0, sizeof(de));
if(writei(dp, 0, (uint64)&de, off, sizeof(de)) != sizeof(de))
panic("unlink: writei");
if(ip->type == T_DIR){
dp->nlink--;
iupdate(dp);
}
iunlockput(dp);
ip->nlink--;
iupdate(ip);
iunlockput(ip);
if(ip->type == T_SYMLINK)
{
iput(ip);
}
end_op();
return 0;
bad:
iunlockput(dp);
end_op();
return -1;
}