fs代码在这里。我的妈呀,终于要写完了,xv6的file system考察难度并不大,这里强烈推荐我工Ext2 Based File System,这里可以给一下参考代码与参考结果,后面找机会写写博客。
xv6中的file system结构与Ext 2的结构类似,如下图所示:
在完成本实验前,推荐阅读xv6 book Chapter 7.9,搞清楚inode的索引结构。
与往常一样,在我的博客OS实验xv6 6.S081 开坑中给出了一些有用的参考资料,大家也可以一并参考。
给出本次实验的实验指导书
本次实验主要需要完成两个部分:
首先要理解inode的结构是什么样的,这里的addrs数组即ext2 file system中的索引指针。根据官方指导书的说法,xv6中的inode有12个直接索引,1个一级索引。另外xv6的数据块大小为1024B,索引指针大小为4B,因此一个数据块可以装256个索引指针,因此,目前一个inode最大支持12 + 256 = 268个数据块。需要注意的是,在xv6中有两种类型的inode,其中一个是存在磁盘中的inode,另一种是缓存在内存中的inode,在对inode结构进行修改的时候需要修改这两处。inode的结构如下:
// On-memory inode structure
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+1];
};
// 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)
/**
* The nlink field counts the number of directory
* entries that refer to this inode,
* in order to recognize when the on-disk
* inode and its data blocks should be freed.
* */
short nlink; // Number of links to inode in file system
/**
* The size field records the number of bytes of content in the file.
*/
uint size; // Size of file (bytes)
/**
* The addrs array records the block numbers of
* the disk blocks holding the file’s content.
*/
uint addrs[NDIRECT+1]; // Data block addresses
};
下图给出了一个基本的inode结构示意图:
本次任务即为xv6中的inode添加一个Doubel Indirect
索引块,这样每一个inode就能够支持11 + 256 + 256 * 256 = 65803个数据块的文件了。实现并不困难,我们首先修改宏定义如下:
/** direct blocks */
#define NDIRECT 11
/** indirect blocks */
#define SINGLEDIRECT (BSIZE / sizeof(uint))
#define NINDIRECT (SINGLEDIRECT + SINGLEDIRECT * BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + NINDIRECT)
其中,NDIRECT
代表直接索引指向块的数量;SINGLEDIRECT
表示一级索引能够指向的块的数量;NINDIRECT
索引代表一级和二级索引一共可指向的块的数量。
其次,根据指导书的Hint:
mkfs initializes the file system to have fewer than 2000 free data blocks, too few to show off the changes you’ll make. Modify kernel/param.h to change FSSIZE from 2000 to 200,000:
#define FSSIZE 200000 // size of file system in blocks
Rebuild mkfs so that is produces a bigger disk: $ rm mkfs/mkfs fs.img; make mkfs/mkfs
我们修改kernel/param.h
中的FSSIZE
宏定义为200000
好了,接下来开始正式实现二级索引列表,代码实现并不困难,只要搞清楚原来的bmap
是如何分配块的就行,在此不做赘述。代码如下:
static uint
bmap(struct inode *ip, uint bn)
{
/**
* bmap() deals with two kinds of block numbers.
* The bn argument is a "logical block number" --
* a block number within the file, relative to the start of the file.
* The block numbers in ip->addrs[], and the argument to bread(),
* are disk block numbers.
* You can view bmap() as
*
*
* mapping a file's logical block numbers into disk block numbers.
*/
// printf("-----------------------------------------------\n");
// printf("bn1: %d\n", bn);
// NINDIRECT 256
// printf("NINDIRECT: %d\n", NINDIRECT); //256
uint addr, *a, *a2;
struct buf *bp, *bp2;
/** bn是相对file(inode)的虚拟编号 */
/** 直接返回块ip->addrs[bn] */
if(bn < NDIRECT){
if((addr = ip->addrs[bn]) == 0)
ip->addrs[bn] = addr = balloc(ip->dev);
return addr;
}
/** 否则减去NDIRENT */
bn -= NDIRECT;
//11 + 256 + 256 * 256
if(bn < NINDIRECT){
if(bn < SINGLEDIRECT){
// Load indirect block, allocating if necessary.
if((addr = ip->addrs[NDIRECT]) == 0)
ip->addrs[NDIRECT] = addr = balloc(ip->dev);
/** Return a locked buf with the contents of the indicated block. */
bp = bread(ip->dev, addr);
/**
* 为何 #define NINDIRECT (BSIZE / sizeof(uint)) ?
* 因为一个buf有BSIZE个data,一个block地址为64位,故刚好存储256个block指针,NINDIRECT = 256
*/
/** 一个文件描述符 */
a = (uint*)bp->data;
if((addr = a[bn]) == 0){
a[bn] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
}
else
{
/**
* 实现double-indirect
* 结构:
* addr -> 256个single-indirect -> 256 * 256个block
* 256 + 11 = 267 0 ~ 266为前面的,267开始后面为后面的
* */
bn -= SINGLEDIRECT;
/** 宏在做乘除运算时记得加括号5555555555555555555 */
/** single-indirect索引*/
int single_indirect_index = bn / SINGLEDIRECT;
/** single-indirect内部相对索引 */
int relative_offset_bn = bn % SINGLEDIRECT;
/** 下标12是double-indirect */
int pos = NDIRECT + 1;
if((addr = ip->addrs[pos]) == 0)
ip->addrs[pos] = addr = balloc(ip->dev);
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if((addr = a[single_indirect_index]) == 0){
printf("bn: %d, addr: %p\n",bn, addr);
a[single_indirect_index] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
bp2 = bread(ip->dev, addr);
a2 = (uint*)bp2->data;
if((addr = a2[relative_offset_bn]) == 0){
a2[relative_offset_bn] = addr = balloc(ip->dev);
log_write(bp2);
}
brelse(bp2);
}
printf("-----------------------------------------------\n");
return addr;
}
panic("bmap: out of range");
}
下面,我们完成itrunc
以释放inode下的所有数据块,同样,做法参考原本的itrunc
即可,代码如下:
static void
itrunc(struct inode *ip)
{
int i, j;
struct buf *bp, *bp2;
uint *a, *a2;
/** Free掉所有Direct */
for(i = 0; i < NDIRECT; i++){
if(ip->addrs[i]){
bfree(ip->dev, ip->addrs[i]);
ip->addrs[i] = 0;
}
}
/** Free掉single-direct */
if(ip->addrs[NDIRECT]){
bp = bread(ip->dev, ip->addrs[NDIRECT]);
a = (uint*)bp->data;
/**
* 修改NINDIRECT为SINGLEDIRECT
* for(j = 0; j < NINDIRECT; j++){
* if(a[j])
* bfree(ip->dev, a[j]);
* }
*/
for (j = 0; j < SINGLEDIRECT; j++)
{
/* code */
if(a[j])
bfree(ip->dev, a[j]);
}
brelse(bp);
bfree(ip->dev, ip->addrs[NDIRECT]);
ip->addrs[NDIRECT] = 0;
}
/** Free掉double-direct */
if(ip->addrs[NDIRECT + 1]){
printf("free double\n");
int pos = NDIRECT + 1;
bp = bread(ip->dev, ip->addrs[pos]);
a = (uint*)bp->data;
int number_of_single_direct = BSIZE / sizeof(uint);
for (i = 0; i < number_of_single_direct; i++)
{
if(a[i]){
bp2 = bread(ip->dev, a[i]);
a2 = (uint *)bp2->data;
for (j = 0; j < SINGLEDIRECT; j++)
{
if(a2[j])
bfree(ip->dev, a2[j]);
}
brelse(bp2);
bfree(ip->dev, a[i]);
a[i] = 0;
}
}
brelse(bp);
bfree(ip->dev, ip->addrs[pos]);
ip->addrs[pos] = 0;
}
ip->size = 0;
iupdate(ip);
}
在完成该实验前,首先需要明白Symbolic Link。可以简单的将它理解为一个存有目标路径的文件,在Windows中可以将其理解为快捷方式。symbolic link文件有两种打开方式,其一是FOLLOW
方式,即打开symbolic link文件内容中的路径;其次是NOFOLLOW
,即将symbolic link文件视为普通文件,直接打开它。
首先分析Hints:
逐条解读:
sys_symlink
系统调用,相关代码如下:user/usys.pl
entry("symlink");
user/user.h
/** Symbol link */
int symlink(char *target, char *path);
kernel/syscall.h
/** Symbol link */
#define SYS_symlink 23
kernel/syscall.c
[SYS_symlink] sys_symlink
knerl/sysfile.c
uint64
sys_symlink(void){
return 0;
}
kernel/stat.h
中添加T_SIMLINK
来表示symbolic link文件kernel/fcntl.h
中添加O_NOFOLLOW
标志,表示将symbolic link文件视为普通文件打开symlink(target,path)
系统调用来创建一个symbolic linkopen
函数,使之能够处理T_SIMLINK
类型的文件;另外还需要判断标志O_NOFOLLOW
已决定是否FOLLOW
。O_NOFOLLOW
的情况下,symbolic link链接的文件还是symbolic link文件,则应该递归寻找直到找到不是symbolic link的文件,将其打开。FOLLOW
symbolic link 文件好了,我们可以开始实现了。首先,补充sys_symlink()
,使之能够创建一个symbolic link,如何创建文件可以参考sys_open
函数中if(omode & O_CREATE)
中的内容。需要注意的是,创建后获得该文件的inode,我们应该释放掉该inode下的全部数据块,调用iunlockput
即可,否则,会出现inodes不足的错误。 代码及相关注释如下:
uint64
sys_symlink(void){
/** 建立软链接文件 */
struct inode *ip;
int target_len, path_len;
char target[MAXPATH], path[MAXPATH];
if((target_len = argstr(0, target, MAXPATH)) < 0 ||
(path_len = argstr(1, path, MAXPATH)) < 0 )
return -1;
begin_op(ROOTDEV);
/**
* Create returns a locked inode, but namei does not
*/
ip = create(path, T_SYMLINK, 0, 0);
if(ip == 0){
/** 这里记得要加 end_op(ROOTDEV),否则会锁死 */
end_op(ROOTDEV);
return -1;
}
if(target_len > MAXPATH)
target_len = MAXPATH;
memset(ip->target, 0, MAXPATH);
memmove(ip->target, target, target_len);
/**
* iunlock(ip)会报错:没有足够的数据块;
* 分配的这个inode可能已经占有了其他数据块,
* 而我们只需要利用inode中的target字段保存target,其他的信息我们一概不用,
* 因此,我们直接调用iunlockput(ip)将ip的所有dircet、indirect字段全部清理掉
* 这样就解决了数据块不够的问题
*/
iunlockput(ip);
end_op(ROOTDEV);
return 0;
}
接下来,我们修改sys_open
函数,使之能够支持打开symbolic link文件。通过观察sys_open
的内容,我们很容易知道可以通过namei
函数获取相应路径对应的inode
,于是我们便可通过递归的方式实现symbolic link。代码如下:
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(ROOTDEV);
if(omode & O_CREATE){
ip = create(path, T_FILE, 0, 0);
printf("### create %s\n",path);
if(ip == 0){
end_op(ROOTDEV);
return -1;
}
} else {
if((ip = namei(path)) == 0){
end_op(ROOTDEV);
return -1;
}
/** Open symlink */
if(ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)){
int counter = 0;
/** 递归查找 */
while ((ip = namei(ip->target)) && ip->type == T_SYMLINK)
{
counter++;
if(counter >= 10){
printf("open(): too many symlink\n");
end_op(ROOTDEV);
return -1;
}
}
if(ip == 0){
end_op(ROOTDEV);
return -1;
}
}
ilock(ip);
......
}