代码在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系统主机,进入修复模式,解决开机报错问题

    1.让主机重启,进入开机时的内核选择界面,按e进入编辑界面 2.找到linux16那一行,将光标移动到最前面,按下End键,到这一行的末尾,然后空格 rd.break console=tty0 3.第 ...

  2. 阿里云OSS对象存储服务(二)

    一.使用SDK 在OSS的概览页右下角找到"Bucket管理",点击"OSS学习路径" 点击"Java SDK"进入SDK开发文档 二.创建 ...

  3. pandas的级联操作

    级联操作 pd.concat, pd.append import pandas as pd from pandas import DataFrame import numpy as np pandas ...

  4. ROS教程(二):创建工作空间(图文)

    ros教程:创建工作空间 目录 前言 一.工作空间? 二.创建一个工作空间 1.创建目录 2.编译 前言 使用catkin创建一个工作空间 一.工作空间? 在ROS系统下,我们所有的项目都放在一个工作 ...

  5. 提供个HDFS的目录的路径,对该目录进行创建和删除操作。创建目录时,如果目录 文件所在目录不存在则自动创建相应目录;删除目录时,由用户指定当该目录不为空时是否还删 除该目录

    import java.io.IOException; import java.util.Scanner; import org.apache.hadoop.fs.*; public class G_ ...

  6. 慕课网金职位 Java工程师2020 百度网盘下载

    百度网盘链接:https://pan.baidu.com/s/1xshLRO3ru0LAsQQ0pE67Qg 提取码:bh9f 如果失效加我微信:610060008[视频不加密,资料代码齐全,超清一手 ...

  7. CF1433F Zero Remainder Sum

    写在前面 思维难度不是很大的 DP,代码实现也很容易. 状态设计模式很套路,转移也很好理解. 算法思路 (因为 \(k\) 是常用的循环变量,下文中将题面中的模数改为 \(p\)) 虽然要求的是模 \ ...

  8. poj2185Milking Grid

    Milking Grid Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 8325   Accepted: 3588 Desc ...

  9. Apache Hudi 0.7.0版本重磅发布

    重点特性 1. Clustering 0.7.0版本中支持了对Hudi表数据进行Clustering(对数据按照数据特征进行聚簇,以便优化文件大小和数据布局),Clustering提供了更灵活地方式增 ...

  10. hashCode、equals详解

    hash和hash表是什么? hash是一个函数,该函数中的实现就是一种算法,就是通过一系列的算法来得到一个hash值,这个时候,我们就需要知道另一个东西,hash表,通过hash算法得到的hash值 ...