代码在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. 【Linux】自建回收站

    linux rm命令是即刻删除的,而且挺多人喜欢加上-f强制命令,更暴力的是删除文件夹直接 rm -rf ,这样子代表你执行完后,就完全被干掉了. 还是推荐在linux下设置回收站,写一个shell脚 ...

  2. Windows程序通用自动更新模块(C#,.NET4.5以上)

    本通用自动更新模块适合所有Windows桌面程序的自动更新,不论语言,无论Winform还是wpf. 一.工作流程:1. 主程序A调起升级程序B2. B从服务器获取更新程序列表,打印更新信息.3. B ...

  3. [XAML] 使用 XAML 格式化工具:XAML Styler

    1. XAML 的问题 刚入门 WPF/UWP 之类的 XAML 平台,首先会接触到 XAML 这一新事物.初学 XAML 时对它的印象可以归纳为一个词:一坨. 随着我在 XAML 平台上工作的时间越 ...

  4. Docker 如何动态修改容器端口映射

    Docker端口映射往往是Docker Run命令时通过-p将容器内部端口映射到宿主机的指定端口上,一般来说容器的端口所对应的端口是提前确定需要映射的.但是有些情况下不得不需要临时映射端口,例如Doc ...

  5. 转 Fiddler1 简单使用

    Fiddler1 简单使用   文章转自:https://www.cnblogs.com/zhengna/p/9008014.html   1.Fiddler下载地址:https://www.tele ...

  6. Xamarin.Forms: 无限滚动的ListView(懒加载方式)

    说明 在本博客中,学习如何在Xamarin.Forms应用程序中设计一个可扩展的无限滚动的ListView.这个无限滚动函数在默认的Xamarin.Forms不存在,因此我们需要为此添加插件.在这里我 ...

  7. 数据水印 watermark

    外发数据创建水印 产品通过对外发数据进行添加数据标记.自动生成水印.数据源追溯等功能,避免了内部人员外发数据泄露无法对事件追溯,提高了数据传递的安全性和可追溯能力. 数据水印系统_数据安全管理工具_[ ...

  8. java中List元素移除元素的那些坑

    https://blog.csdn.net/javageektech/article/details/96668890  List  的迭代器类 采用倒序移除 jdk1.8的写法 public sta ...

  9. Map转换为格式化的YAML字符串

    yaml与java对象的互转 yaml与java对象的互转有snakeyaml <dependency> <groupId>org.yaml</groupId> & ...

  10. 函数式编程(json、pickle、shelve)

    本节内容 前言 json模块 pickle模块 shelve模块 总结 一.前言 1. 现实需求 每种编程语言都有各自的数据类型,其中面向对象的编程语言还允许开发者自定义数据类型(如:自定义类),Py ...