代码在github上。

这次实验是要对文件系统修改,使其支持更大的文件以及符号链接,实验本身并不是很复杂。但文件系统可以说是XV6中最复杂的部分,整个文件系统包括了七层:文件描述符,路径名,目录,inode,日志,缓冲区,磁盘。

文件描述符类似于Linux,将文件、管道、设备、套接字等都抽象为文件描述符,从而可以使用readwrite系统调用对其进行读写。XV6的readwrite是使用if-else来对描述符类型进行判断,选择对应的底层函数;而在Linux中,则是使用函数指针直接指向对应的底层函数,避免进行多次判断。

路径名则提供了根据路径名从目录系统中查找文件的功能。在路径查找过程中需要避免可能会出现的死锁,例如路径名中包含..

目录层类似于文件,目录文件的内部会保存该目录的目录项struct dirent,其中包含了文件名和对应的inode号。在XV中目录查找是使用遍历目录项数组来依次比较,时间复杂度为O(n);而在NTFS、ZFS等文件系统中,会使用磁盘平衡树来组织目录项,使目录查找的复杂度降低为O(lgn)。

inode层为文件在磁盘上的组织,在磁盘中会有一块区域用于保存inode信息,包括文件类型、大小、链接数以及文件每个块对应的磁盘块号。通过路径从目录系统中查找到对应的inode号,之后就可以从磁盘上读取对应的inode信息,之后就可以根据偏移量查找对应的磁盘块号,最后对其进行读写。

日志层提供了事务以及故障恢复的功能,当有多个磁盘操作必须原子完成时就要用到事务(如删除文件时要从目录中删除文件,删除文件对应的inode,对空闲块bitmap进行修改等)。日志先将操作写到磁盘的日志区上,写入完成后再写入commit,最后再将所有操作真正写到磁盘上去。当在写入commit之前发生故障,就不需要进行操作,因为事务没有被提交;当在写入commit之后发生故障,就将日志区的日志全部重写一遍,保证事务被正确提交。

缓冲区则提供了磁盘块缓存,同时保证一个磁盘块在缓冲区中只有一个,使得同一时间只能有一个线程对同一个块进行操作,避免读到的数据不一致。

Large files (moderate)

这一个实验是要使XV6支持更大的文件。原始XV6中的文件块号dinode.addr是使用一个大小为12的直接块表以及一个大小为256的一级块表,即文件最大为12+256块。可以通过将一个直接块表中的项替换为一个二级块表来使系统支持大小为11+256+256*256个块的文件。

首先修改对应的宏以及inode定义。

#define NDIRECT 11
#define NINDIRECT (BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + NINDIRECT + NINDIRECT * NINDIRECT) struct dinode {
...
uint addrs[NDIRECT+2]; // Data block addresses
}; struct inode {
...
uint addrs[NDIRECT+2]; // Data block addresses
};

之后修改bmap函数,使其支持二级块表,其实就是重复一次块表的查询过程。

static uint
bmap(struct inode *ip, uint bn)
{
...
bn -= NINDIRECT; if(bn < NINDIRECT * NINDIRECT){
// double indirect
int idx = bn / NINDIRECT;
int off = bn % 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[idx]) == 0){
a[idx] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp); bp = bread(ip->dev, addr);
a = (uint*)bp->data;
if((addr = a[off]) == 0){
a[off] = addr = balloc(ip->dev);
log_write(bp);
}
brelse(bp);
return addr;
} panic("bmap: out of range");
}

最后修改itrunc函数使其能够释放二级块表对应的块,主要就是注意一下brelse的调用就行了,仿照一级块表的处理就行了。

void
itrunc(struct inode *ip)
{
...
if(ip->addrs[NDIRECT + 1]){
bp = bread(ip->dev, ip->addrs[NDIRECT + 1]);
a = (uint*)bp->data; struct buf *bpd;
uint* b;
for(j = 0; j < NINDIRECT; j++){
if(a[j]){
bpd = bread(ip->dev, a[j]);
b = (uint*)bpd->data;
for(int k = 0; k < NINDIRECT; k++){
if(b[k])
bfree(ip->dev, b[k]);
}
brelse(bpd);
bfree(ip->dev, a[j]);
}
}
brelse(bp);
bfree(ip->dev, ip->addrs[NDIRECT + 1]);
ip->addrs[NDIRECT + 1] = 0;
} ip->size = 0;
iupdate(ip);
}

Symbolic links (moderate)

这一个实验是要实现符号链接,符号链接就是在文件中保存指向文件的路径名,在打开文件的时候根据保存的路径名再去查找实际文件。与符号链接相反的就是硬链接,硬链接是将文件的inode号指向目标文件的inode,并将引用计数加一。

symlink的系统调用实现起来也很简单,就是创建一个inode,设置类型为T_SYMLINK,然后向这个inode中写入目标文件的路径就行了。

uint64
sys_symlink(void)
{
char target[MAXPATH];
memset(target, 0, sizeof(target));
char path[MAXPATH];
if(argstr(0, target, MAXPATH) < 0 || argstr(1, path, MAXPATH) < 0){
return -1;
} struct inode *ip; begin_op();
if((ip = create(path, T_SYMLINK, 0, 0)) == 0){
end_op();
return -1;
} if(writei(ip, 0, (uint64)target, 0, MAXPATH) != MAXPATH){
// panic("symlink write failed");
return -1;
} iunlockput(ip);
end_op();
return 0;
}

最后在sys_open中添加对符号链接的处理就行了,当模式不是O_NOFOLLOW的时候就对符号链接进行循环处理,直到找到真正的文件,如果循环超过了一定的次数(10),就说明可能发生了循环链接,就返回-1。这里主要就是要注意namei函数不会对ip上锁,需要使用ilock来上锁,而create则会上锁。

uint64
sys_open(void)
{
...
if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){
...
} if(ip->type == T_SYMLINK){
if(!(omode & O_NOFOLLOW)){
int cycle = 0;
char target[MAXPATH];
while(ip->type == T_SYMLINK){
if(cycle == 10){
iunlockput(ip);
end_op();
return -1; // max cycle
}
cycle++;
memset(target, 0, sizeof(target));
readi(ip, 0, (uint64)target, 0, MAXPATH);
iunlockput(ip);
if((ip = namei(target)) == 0){
end_op();
return -1; // target not exist
}
ilock(ip);
}
}
} if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
...
}

XV6学习(14)Lab fs: File system的更多相关文章

  1. MIT 6.S081 Lab File System

    前言 打开自己的blog一看,居然三个月没更新了...回想一下前几个月,开题 + 实验室杂活貌似也没占非常多的时间,还是自己太懈怠了吧,掉线城和文明6真的是时间刹手( 不过好消息是把15445的所有l ...

  2. MIT6.828 La5 File system, Spawn and Shell

    Lab 5: File system, Spawn and Shell 1. File system preliminaries 在lab中我们要使用的文件系统比大多数"真实"文件 ...

  3. MIT-6.828-JOS-lab5:File system, Spawn and Shell

    Lab 5: File system, Spawn and Shell tags: mit-6.828 os 概述 本lab将实现JOS的文件系统,只要包括如下四部分: 引入一个文件系统进程(FS进程 ...

  4. java hadoop file system API

    org.apache.hadoop.fs Class FileSystem java.lang.Object org.apache.hadoop.fs.FileSystem All Implement ...

  5. 9. Lab: file system

    https://pdos.csail.mit.edu/6.S081/2021/labs/fs.html 1. Large files (moderate) 1.1 要求 Modify bmap() s ...

  6. 学习 google file system 心得体会

    Google File system文件系统,是在特别便宜的普通硬件设备上运行,它是一个面向大规模数据密集型运用的.可伸缩的分布式文件系统. 与传统文件相比,它认为组件失效是很平常的事件,因为GFS包 ...

  7. Google File System 学习

    摘要 Google的人设计并实现了Google File System,一个可升级的分布式文件系统,用于大的分布式数据应用.可以运行在廉价的日用硬件上,具备容错性,且为大量客户端提供了高聚合的性能. ...

  8. xv6学习笔记(4) : 进程调度

    xv6学习笔记(4) : 进程 xv6所有程序都是单进程.单线程程序.要明白这个概念才好继续往下看 1. XV6中进程相关的数据结构 在XV6中,与进程有关的数据结构如下 // Per-process ...

  9. Linux File System

    目录 . Linux文件系统简介 . 通用文件模型 . VFS相关数据结构 . 处理VFS对象 . 标准函数 1. Linux文件系统简介 Linux系统由数以万计的文件组成,其数据存储在硬盘或者其他 ...

随机推荐

  1. qmake奇淫技巧之字符串宏定义

    阅读本文大概需要3.3分钟 我们平时在软件开发过程中需要定义一些宏,以便在代码中调用,这样每次不需要修改代码,只需要修改外部编译命令就可以得到想要的参数,非常方便 比如我们想在软件介绍中显示软件版本, ...

  2. 设计一款兼容ST207和GD207的开发板

    在MCU的学习中,大部分人都是学习别人的开发板,例如正点原子.野火等,优点是有可靠的教程和代码,缺点是容易让人有种自己全部都学会的了错觉,听了课程编写了代码,运行正常. 这个时候,可以尝试自已做一块属 ...

  3. jQuery库 之 jquery slimscroll插件使用

    1.引入jQuery插件 <script type="text/javascript" src="jquery.min.js"></scrip ...

  4. (17)-Python3之--文件操作

    1.文件的操作流程 第一,建立文件对象. 第二,调用文件方法进行操作. 第三,不要忘了关闭文件.(文件不关闭的情况下,内容会放在缓存,虽然Python会在最后自动把内容读到磁盘,但为了以防万一,要养成 ...

  5. 如何用OKR促进跨团队协同

    https://mp.weixin.qq.com/s/347dKRlez0_KJKGOkTI0AQ

  6. git commit前检测husky与pre-commit 提交钩子

    git commit前检测husky与pre-commit git commit前检测husky与pre-commit - 简书 https://www.jianshu.com/p/f0d31f92b ...

  7. Linux命令——netcat

    简介 netcat的简写是nc,被设计为一个简单.可靠的网络工具,主要作用如下: 1 实现任意TCP/UDP端口的侦听,nc可以作为server以TCP或UDP方式侦听指定端口 2 端口的扫描,nc可 ...

  8. Asp.netCore 3.1控制器属性注入and异步事务Aop by AutoFac

    Aspect Oriented Programming(AOP)是较为热门的一个话题.AOP,国内我们都习惯称之为:面向切面编程 下面直接code 干货展示:(一般人我还不告诉,嘻嘻) 1:导入相关的 ...

  9. Hash Map集合和Hash Set集合

    HashMap集合的使用 1.1.每个集合对象的创建(new) 1.2.从集合中添加元素 1.3.从集合中取出某个元素 1.4.遍历集合 public class HashMapTest { publ ...

  10. CQOI 2006 简单题

    CQOI 2006 简单题 有一个 n 个元素的数组,每个元素初始均为 0.有 m 条指令,要么让其中一段连续序列数字反转--0 变 1,1 变 0(操作 11),要么询问某个元素的值(操作 2). ...