写在前面
文件系统是操作系统对磁盘的抽象,提供相应的API接口,以便于用户使用。在xv6中文件系统一共分为7个层次,如下图所示。最下层是最基础的硬件磁盘,是文件数据直接存储的场所。磁盘具有非易失性的特点,即存储在磁盘上的数据不会因为断电而丢失。磁盘之上是一个缓存,主要作用是缓存常用的磁盘块,从而减少读取磁盘而带来的开销。
缓存之上是日志,其主要作用是为了解决系统崩溃的问题。操作系统中文件操作相关的系统调用通常是由多步的磁盘操作组成的,如果在执行文件系统调用时发生了崩溃,可能会导致磁盘中数据的丢失与错误。日志解决这个问题的基本思想是,通过把文件操作所需要的磁盘操作写入到log块中,只有当所有的磁盘操作都写入完毕后,再提交,并记录磁盘操作所需要的数目;提交完毕后,可以进行真正的磁盘写入操作。当这些操作完成后,将操作计数清零,并清楚日志。如果发生了系统崩溃,重启后若发现操作计数为0,那么相当于文件操作未发生;若操作计数不为0,则重新执行在日志中所有的写入操作。 这样做确保了所有的磁盘操作是原子化的。
日志之上是Inode层。inode记录了文件的硬链接数、文件大小、文件存储的块数以及文件存储的块编号。每次需要创建、读取以及写入文件都需要分配或者读取inode。目录是对inode的进一步抽象,为每一个inode分配了一个目录名。若一个inode有n个目录指向它,那么inode的硬链接数就为n。目录的名字组成了路径名,每个目录名字间用“/”分隔,便于用户通过路径名查找对应的inode。文件描述符则是对路径名进一步的抽象,用户层面主要是通过使用文件描述符来对文件进行操作。
实验部分
一 Large File
xv6中,一个inode总共记录了13个块编号,前12个是直接引用块,后1个是间接引用块。在xv6中一个块编号是4B,一个块是1024B,故一共有1024/4=256个,故总共有268个引用块,因此一个文件最大的大小是268x1KB=268KB。在如今的计算机系统中,268KB无法存储大型的文件,因此需要能够存储更加大的文件。一个办法是将其中一个块编号作为double-indirectly的块,即该块存储256个间接引用块,每个间接引用块可以存储256个文件块,故一共有256*256个引用块,因此一个文件则可以存储(256*256+256+11)个块,即65803个块,大小为65803KB。具体代码如下:
1 在fs.h文件中,将NDIRECT设置为11,将MAXFILE设置为NDIRECT + NINDIRECT+NINDIRECT*NINDIRECT,并在struct dinode中将addrs数组的长度置为NDIRECT+2;以及在file.h中struct inode中addrs数组长度置为NDIRECT+2。
//fs.h
#define NDIRECT 11
#define NINDIRECT (BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + NINDIRECT+NINDIRECT*NINDIRECT)
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
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];
};
2 bmap中通过块编号访问对应的块,itruct中对块进行释放。对块进行如下编号:
0-10:前11块为直接引用块
11-266:间接引用块
267-65802:double-indirectly块
static uint
bmap(struct inode *ip, uint bn)
{
uint addr, *a;
struct buf *bp,*bp2;
if(bn < NDIRECT){
if((addr = ip->addrs[bn]) == 0)
ip->addrs[bn] = addr = balloc(ip->dev);
return addr;
}
bn -= NDIRECT;
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<NINDIRECT*NINDIRECT){
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[bn/256])==0){
a[bn/256]=addr=balloc(ip->dev);
log_write(bp);
}
bp2=bread(ip->dev,addr);
a=(uint*)bp2->data;
if((addr=a[bn%256])==0){
a[bn%256]=addr=balloc(ip->dev);
log_write(bp2);
}
brelse(bp2);
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;
struct buf *bp,*bp2;
uint *a;
for(i = 0; i < NDIRECT; i++){
if(ip->addrs[i]){
bfree(ip->dev, ip->addrs[i]);
ip->addrs[i] = 0;
}
}
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;
}
if(ip->addrs[NDIRECT+1]){
bp=bread(ip->dev,ip->addrs[NDIRECT+1]);
a=(uint*)bp->data;
for(int i=0;i<NINDIRECT;i++){
if(a[i]){
bp2=bread(ip->dev,a[i]);
uint* a2=(uint*)bp2->data;
for(j=0;j<NINDIRECT;j++){
if(a2[j])
bfree(ip->dev,a2[j]);
}
brelse(bp2);
bfree(ip->dev,a[i]);
}
}
brelse(bp);
bfree(ip->dev,ip->addrs[NDIRECT+1]);
}
ip->size = 0;
iupdate(ip);
}
二 Symbolic links
symbolic links也叫做软链接。硬链接是目录名直接指向对应文件的inode,软链接是将目录名链接到一个额外的inode,但是inode中对应的块存储的是需要软链接的文件的路径名。因此,如果软链接到的文件被删除了,那么就没办法通过软链接打开。
1 创建symlink系统调用相关的代码
//user/usys.pl
entry("symlink");
//user/user.h
int symlink(char*,char*);
//kernel/syscall.c
extern uint64 sys_symlink(void);
[SYS_symlink] sys_symlink,
//kernel/syscall.h
#define SYS_symlink 22
2 添加T_SYMLINK到kernel/stat.h文件中
#define T_SYMLINK 4 // Symbolic link
3 添加O_NOFOLLOW到kernel/fcntl.h中,表示打开一个软链接文件时,不去追溯其源
#define O_NOFOLLOW 0x800
4 实现sys_symlink系统调用。
uint64
sys_symlink(void){
char target[MAXPATH],path[MAXPATH],name[DIRSIZ];
struct inode *dp,*sp;
if(argstr(0,target,MAXPATH)<0 || argstr(1,path,MAXPATH)<0)
return -1;
begin_op();
if((dp=nameiparent(path,name))==0){
end_op();
return -1;
}
sp=ialloc(dp->dev,T_SYMLINK);
ilock(sp);
if(writei(sp,0,(uint64)target,0,sizeof(target))!=sizeof(target)){
iunlockput(sp);
iput(dp);
end_op();
return -1;
}
ilock(dp);
if(dirlink(dp,name,sp->inum)<0){
iunlockput(dp);
iunlockput(sp);
end_op();
return -1;
}
iunlockput(dp);
sp->nlink++;
iupdate(sp);
iunlockput(sp);
end_op();
return 0;
}
5 sys_open系统调用中,当路径名是T_SYMLINK且打开的mode不是O_NOFOLLOW,那么就需要打开路径名软链接到的文件。如果软链接构成了环,则打开文件失败,需要用一个计数器cnt表示软链接的数,若cnt超过10,那么可以认为构成了环。
uint64
getsymlink(struct inode *ip,struct inode **res){
struct inode *p=ip;
int cnt=0;
do{
char target[MAXPATH];
readi(p,0,(uint64)target,0,MAXPATH);
iunlockput(p);
if((p=namei(target))==0){
return -1;
}
cnt++;
ilock(p);
}while(cnt<10 && p->type==T_SYMLINK);
if(cnt==10) return -1;
*res=p;
return 0;
}
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 = 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 && omode!=O_NOFOLLOW){
struct inode *ip2=ip;
if(getsymlink(ip2,&ip)!=0){
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;
}
三 实验结果