XV6学习(14)Lab fs: File system
代码在github上。
这次实验是要对文件系统修改,使其支持更大的文件以及符号链接,实验本身并不是很复杂。但文件系统可以说是XV6中最复杂的部分,整个文件系统包括了七层:文件描述符,路径名,目录,inode,日志,缓冲区,磁盘。
文件描述符类似于Linux,将文件、管道、设备、套接字等都抽象为文件描述符,从而可以使用read
和write
系统调用对其进行读写。XV6的read
和write
是使用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的更多相关文章
- MIT 6.S081 Lab File System
前言 打开自己的blog一看,居然三个月没更新了...回想一下前几个月,开题 + 实验室杂活貌似也没占非常多的时间,还是自己太懈怠了吧,掉线城和文明6真的是时间刹手( 不过好消息是把15445的所有l ...
- MIT6.828 La5 File system, Spawn and Shell
Lab 5: File system, Spawn and Shell 1. File system preliminaries 在lab中我们要使用的文件系统比大多数"真实"文件 ...
- 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进程 ...
- java hadoop file system API
org.apache.hadoop.fs Class FileSystem java.lang.Object org.apache.hadoop.fs.FileSystem All Implement ...
- 9. Lab: file system
https://pdos.csail.mit.edu/6.S081/2021/labs/fs.html 1. Large files (moderate) 1.1 要求 Modify bmap() s ...
- 学习 google file system 心得体会
Google File system文件系统,是在特别便宜的普通硬件设备上运行,它是一个面向大规模数据密集型运用的.可伸缩的分布式文件系统. 与传统文件相比,它认为组件失效是很平常的事件,因为GFS包 ...
- Google File System 学习
摘要 Google的人设计并实现了Google File System,一个可升级的分布式文件系统,用于大的分布式数据应用.可以运行在廉价的日用硬件上,具备容错性,且为大量客户端提供了高聚合的性能. ...
- xv6学习笔记(4) : 进程调度
xv6学习笔记(4) : 进程 xv6所有程序都是单进程.单线程程序.要明白这个概念才好继续往下看 1. XV6中进程相关的数据结构 在XV6中,与进程有关的数据结构如下 // Per-process ...
- Linux File System
目录 . Linux文件系统简介 . 通用文件模型 . VFS相关数据结构 . 处理VFS对象 . 标准函数 1. Linux文件系统简介 Linux系统由数以万计的文件组成,其数据存储在硬盘或者其他 ...
随机推荐
- 【Oracle】Oracle SQL的优化软件
对于SQL开发人员和DBA来说,根据业务需求写出一条正确的SQL很容易.但是SQL的执行性能怎么样呢?能优化一下跑得更快吗?如果不是资深 DBA,估计很多人都没有信心. 幸运的是,自动化优化工具可以 ...
- kubernets之存活探针
一 存活探针存在的意义 1.1 kubernet通过存活探针(liveness probe)检查容器是否还在运行,可以为pod中的每个容器单独指定存活探针,如果探针执行失败,kubernets会 ...
- dmp文件导入抽取方法
一.确认dmp文件.oracle客户端和服务端的字符集 (1)dmp文件字符集确认: 使用UE打开dmp文件查看文件第2个和第3个字节内容,这两个字节记录了dmp文件的字符集.如0354,然后用以下s ...
- vue路由切换和用location切换url的区别
最近的业务涉及到了axios的拦截器,要在request.js里面要根据状态码来跳转页面,这时候我就面对了几种跳转选择: 1.使用location.href='/url'来跳转,简单方便,但是刷新了页 ...
- BAPI_MATERIAL_BOM_GROUP_CREATE创建BOM
天使用BAPI函数:BAPI_MATERIAL_BOM_GROUP_CREATE创建BOM组数据,过程中发现很多问题啊.总结如下: BOMGROUP VARIANTS 这两个参数必须要传 ...
- uni-app 开发随笔(踩坑记录)
这里总结一些uni-app开发时我遇到的坑 uni-app获取元素高度及屏幕高度(uni-app不可使用document) uni.getSystemInfo({ success: function( ...
- 白日梦的Elasticsearch实战笔记,32个查询案例、15个聚合案例、7个查询优化技巧。
目录 一.导读 三._search api 搜索api 3.1.什么是query string search? 3.2.什么是query dsl? 3.3.干货!32个查询案例! 四.聚合分析 4.1 ...
- uwsgi 启动django
1, django 官方文档可配置项如下: 2,启动django 的配置: 1,和settings.py 同级目录下新建wsgi.py (该配置和manager.py 的配置基本是一样的) impo ...
- k8s之PV、PVC、StorageClass详解
导读 上一篇写了共享存储的概述以及一个简单的案例演示.这一篇就写一下PV和PVC. PV是对底层网络共享存储的抽象,将共享存储定义为一种"资源",比如Node也是容器应用可以消费的 ...
- 《Effective C#》之减少装箱和拆箱
<Effective C#>之减少装箱和拆箱_天极网 http://dev.yesky.com/msdn/359/3486359.shtml <Effective C#>之减少 ...