lab9 fs

image-20230829122900742

文章目录

  • Large files
    • task
    • hints
    • 思路
  • Symbolic links
    • task
    • hints
    • 思路
      • sys_symlink
      • sys_open

Large files

目标:11+256+256*256个block

inode的格式在fs.hstruct dinode中被定义,你需要特别注意以下几点

  1. NDIRECT
  2. NINDIRECT
  3. MAXFILE
  4. addrs[]

在磁盘上找一个文件数据是通过fs.c中的bmap()实现的

  1. 无论是读还是写文件,都调用了bmap
  2. 在写文件时,bmap()分配了新的block去容纳文件内容,在必要的时候,会去分配一个非直接映射块

bmap处理了两种块号

  1. bn参数是一个逻辑块号,是在一个文件中相对于文件起始位置的块号
  2. ip->addrs[]bread()的参数中的块号,都是磁盘块号
  3. 你可以将bmap看做是逻辑块号到物理块号的映射

task

  1. 修改bmap使其通过addrs[]支持11+256+256*256个磁盘块
  2. 如果能通过bigfileusertests测试,就说明成功

hints

  1. 保证你理解了bmap()。画图理清楚inode中不同类型的块的指向和作用
  2. 想一下你如何通过逻辑块号索引一级地址块和直接地址块
  3. 如果你改变了NDIRECT,你可能需要去改变file.hstruct inode中的addrs[]的声明。保证struct inodestruct dinode在addrs数组中有相同数量的元素
  4. 如果你改变了NDIRECT的定义,保证你创造了一个新的fs.img,即make clean 然后make qemu
  5. 对任何一个block进行bread之后都要记得brelse
  6. 你应该只在必要的时候分配一级地址和二级地址
  7. 保证itrunc将一个文件所有的block都free了,包括新增的二级地址

思路

文件系统这一块,感觉学的很难,各种函数很多,但是这个task这一块是不太难,不过我也做了好久。。。

这个task只需要修改bmapitrunc两个函数,以及一些宏常量,之所以只修改这么点东西就可以给一个文件扩容,应该是因为其他函数都是通过bmap来获取逻辑块对应的物理块号的,它们只负责要和写,根本不管到底使用了多少block

首先,需要修改一些宏常量,并且将dinodeinodeaddrs数组长度修改

#define NDIRECT 11
#define NINDIRECT (BSIZE / sizeof(uint))
#define N2INDIRECT (NINDIRECT * NINDIRECT)
#define MAXFILE (NDIRECT + NINDIRECT + N2INDIRECT)uint addrs[NDIRECT + 2];

然后,修改bitmap函数,首先可以看一下bitmap如何处理直接地址和一级地址,学习一下基本的思路,我们这里基本就是嵌套一下一级地址的情况。

具体实现如下:

  1. 首先,将逻辑块号减去一级地址的块数

  2. 然后这里使用了一个search函数

    uint search(struct inode *ip, uint index, uint bn, uint *addrs)

    这个函数的意思是,目前寻找的文件的inode是ip,现在要去addrs数组的index项指向的那个多级地址块上的第bn个block的地址,如果第bn块处没有地址,那么就创建一个。所以这个本质上就是一个一级地址的情况,通过两次调用这个函数,就可以完成我们二级地址的查找

代码如下

uint search(struct inode *ip, uint index, uint bn, uint *addrs) {uint addr, *a;struct buf *bp;if ((addr = addrs[index]) == 0) {addrs[index] = addr = balloc(ip->dev);}bp = bread(ip->dev, addr);a = (uint *)bp->data;if ((addr = a[bn]) == 0) {a[bn] = addr = balloc(ip->dev);log_write(bp);}brelse(bp);return addr;
}static uint
bmap(struct inode *ip, uint bn) {uint addr, *a;struct buf *bp;if (bn < NDIRECT) {if ((addr = ip->addrs[bn]) == 0)ip->addrs[bn] = addr = balloc(ip->dev);return addr;}bn -= NDIRECT;if (bn < NINDIRECT) {// Load indirect block, allocating if necessary.if ((addr = ip->addrs[NDIRECT]) == 0)ip->addrs[NDIRECT] = addr = balloc(ip->dev);bp = bread(ip->dev, addr);a = (uint *)bp->data;if ((addr = a[bn]) == 0) {a[bn] = addr = balloc(ip->dev);log_write(bp);}brelse(bp);return addr;}bn -= NINDIRECT;if (bn < N2INDIRECT) {int index = bn / NINDIRECT;int nbn = bn % NINDIRECT;addr = search(ip, NDIRECT + 1, index, ip->addrs);bp = bread(ip->dev, ip->addrs[NDIRECT + 1]);addr = search(ip, index, nbn, (uint *)bp->data);brelse(bp);return addr;}panic("bmap: out of range");
}

Symbolic links

task

  1. 增加一个系统调用symlink(char *target, char *path)
  2. 需要通过symlinktestusertests

hints

  1. 增加系统调用的流程

    1. Makefile,加入的不是symlink,而是symlinktest
    2. user/usys.pl
    3. user/user.h
    4. kernel/sysfile.c
    5. syscall.h && syscall.c
  2. kernel/stat.h中增加一个新的文件类型T_SYMLINK代表软链接

  3. kernel/fcntl.h中增加一个标志位O_NOFOLLOW,因为open文件的标志位是or起来的,因此你不能和已有的发生重叠

  4. 你需要找一个位置去存储软链接的目标地址,例如在inode数据块

    symlink应该返回0表示成功,返回-1表示失败

  5. 修改open系统调用去处理一个路径指向软链接的情况

    如果文件不存在,open必须失败

    当一个进程在open中指定了O_NOFOLLOW,则说明不是打开target,而是打开软链接

  6. 如果被链接的文件也是一个软链接,你必须递归地访问,直到访问一个正确的文件

    你可以定义最大递归的层数,比如10

  7. Other system calls (e.g., link and unlink) must not follow symbolic links; these system calls operate on the symbolic link itself.

  8. 你不需要处理软链接到目录的情况

思路

这玩意看着很抽象,但是其实搞清楚以下几件事就行了

  1. 访问文件就是先访问得到inode,然后通过inode去写对应的文件

    通过readi就可以读取path对应的inode

    通过writei就可以在inode对应的文件中去写

  2. 软链接的作用

    在open它的时候,它会直接导向target

  3. 创建软链接,分为以下几步

    1. 首先创建一个文件,即获得一个inode,这个可以通过create函数实现
    2. 将我们的target写入这个inode,我们就将target存在第一个文件数据块就行了
  4. 打开软链接对应的文件,分为以下几步

    1. 在open中获取软链接对应的真实的inode
    2. 然后就让open对这个inode进行分配fd和file的操作即可

代码很少,但是思路真的很有意思

sys_symlink

这里可以先看看create和open的代码是如何使用xv6提供的一些api的,主要是

create,writei,readi

  1. 首先,我们需要将target和path这两个参数从寄存器中读出来,使用argstr即可
  2. 然后,我们需要创建inode,使用create函数,第一个参数是软链接的路径,第二个参数是文件类型,我们这里当然是新建的那个,后面两个参数不知道啥意思,模仿其他函数的使用,填0
  3. 将target写入软链接文件,也就是写入数据块,使用writei函数
    1. 第一个参数是inode的指针
    2. 第三个参数是我们写入的东西的地址,这里就是target的地址
    3. 第四个参数是写到文件的哪里,其实就是使用一个偏移量完成,我们软链接文件没其他的文件内容,就写到偏移量为0的地方,也就是文件的起始位置
    4. 最后一个参数是写入多少个字节
    5. 注意,这里如果操作失败了,需要将这个inode的锁给解开了

bug:没有正确判断函数的返回值,说的就是writei,主要是因为writei的参数太多了,当我一个一个填完参数之后,就忘记判断它的返回值是否小于0了

sys_symlink(void) {char target[MAXPATH], path[MAXPATH];if (argstr(0, target, MAXPATH) < 0) {return -1;}if (argstr(1, path, MAXPATH) < 0) {return -1;}struct inode *ip;// 创建软链接文件的inodebegin_op();if ((ip = create(path, T_SYMLINK, 0, 0)) == 0) {end_op();return -1;}// 将target写入软链接的数据块中if (writei(ip, 0, (uint64)target, 0, strlen(target)) < 0) {iunlockput(ip);end_op();return -1;}iunlockput(ip);end_op();return 0;
}

这里还有一些细节,比如begin_opend_op,比如create之后是会自动给inode上锁的

sys_open

这一个函数的修改就是对应我们真正使用软链接的情况

如果我们设置了O_NOFOLLOW,那说明不是访问target,就是想访问这个软链接,那就正常open就行了

而如果我们没设置,说明实际上要访问的是target,在这种情况下,我们只需要在open函数分配fd和file之前,将ip指针切换成target的ip地址即可,因此,找一个适当的位置截胡即可。我这里选择的是在获取已有文件的inode时进行的

  1. 首先,如果进入了else分支,都进入这个while循环,这个while循环走来就读取path的inode,如果不是软链接或者不是需要target的情况,那就直接break,这样的话就和之前的open一样了
  2. 如果需要找target,那就会读出当前软链接文件的target,然后解锁当前inode,进入下一轮while循环,获取target的inode,如果还是软链接,则递归操作,这里是通过迭代代替递归
    1. 这中间关键的函数是readi函数,我看了下实现,具体的操作其实看不太懂。这里有个小问题,那就是最后一个参数应该传入的是我们想读入的path的长度,但是我们这里不知道path多长,只能传入MAXPATH。这样有没有可能多读了呢?我估计是因为这些数据块的没有被write的地方都是0,那么多读一点正好还给path当结尾0了
    if (omode & O_CREATE) {ip = create(path, T_FILE, 0, 0);if (ip == 0) {end_op();return -1;}} else {int depth = 0;while (1) {if ((ip = namei(path)) == 0) {end_op();return -1;}ilock(ip);if ((ip->type == T_SYMLINK) && (!(omode & O_NOFOLLOW))) {if (++depth > 10) {iunlockput(ip);end_op();return -1;}if (readi(ip, 0, (uint64)path, 0, MAXPATH) < 0) {iunlockput(ip);end_op();return -1;}iunlockput(ip);} else {break;}}if (ip->type == T_DIR && omode != O_RDONLY) {iunlockput(ip);end_op();return -1;}}

这个lab说简单也简单,说难也难,主要是我人菜还不愿意慢慢学

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/88096.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT癌症治疗“困难重重”,真假混讲难辨真假,准确有待提高

近年来&#xff0c;人工智能在医疗领域的应用逐渐增多&#xff0c;其中自然语言处理模型如ChatGPT在提供医疗建议和信息方面引起了广泛关注。然而&#xff0c;最新的研究表明&#xff0c;尽管ChatGPT在许多领域取得了成功&#xff0c;但它在癌症治疗方案上的准确性仍有待提高。…

如何将储存在Mac或PC端的PDF文件传输到移动设备呢?

iMazing是一款iOS设备管理软件&#xff0c;用户借助它可以将iPad或iPhone上的文件备份到PC或Mac上&#xff0c;还能实现不同设备之间的文件传输&#xff0c;能很大程度上方便用户进行文件管理。 在阅读方面&#xff0c;iPad和iPhone是阅读PDF的优秀选择&#xff0c;相较于Mac或…

如何制作党建专题汇报片

通过展示党组织的凝聚力和战斗力&#xff0c;增强党员的组织归属感和团结合作意识。通过宣传片&#xff0c;可以加强党组织的凝聚力&#xff0c;推动党的事业发展。制作党建专题汇报片需要一定的前期准备和后期制作技巧。下面是由深圳党建专题汇报片制作公司老友记小编为您整理…

Java实现根据关键词搜索1688商品新品数据方法,1688API节课申请指南

要通过1688的API获取商品新品数据&#xff0c;您可以使用1688开放平台提供的接口来实现。以下是一种使用Java编程语言实现的示例&#xff0c;展示如何通过1688开放平台API获取商品新品数据&#xff1a; 首先&#xff0c;确保您已注册成为1688开放平台的开发者&#xff0c;并创…

【Go 基础篇】切片:Go语言中的灵活数据结构

在Go语言中&#xff0c;切片&#xff08;Slice&#xff09;是一种强大且灵活的数据结构&#xff0c;用于管理和操作一系列元素。与数组相比&#xff0c;切片的大小可以动态调整&#xff0c;这使得它成为处理动态数据集合的理想选择。本文将围绕Go语言中切片的引入&#xff0c;介…

Vue2向Vue3过度核心技术工程化开发和脚手架

目录 1 工程化开发和脚手架1.1 开发Vue的两种方式1.2.脚手架Vue CLI 2 项目目录介绍和运行流程2.1 项目目录介绍2.2 运行流程 3 组件化开发4 根组件 App.vue4.1 根组件介绍4.2 组件是由三部分构成4.3 总结 5 普通组件的注册使用-局部注册5.1 特点&#xff1a;5.2 步骤&#xff…

Kubernetes(k8s)当中安装并使用ingress暴露应用

Kubernetes当中安装并使用ingress暴露应用 为什么需要Ingress前期准备集群准备LoadBalancer准备 安装Ingress-Nginx下载地址v1.3.1v1.8.1 修改文件v1.3.1v1.8.1修改ingress服务类型配置 执行安装 部署应用通过ingress-nginx暴露应用部署ingress的yaml文件v1.3.1v1.8.1 为什么需…

大学生该怎么认清当下的就业环境呢?

大学生毕业后进入职场&#xff0c;面临的就业环境也在不断发生变化。为了更好地适应这个变化莫测的环境&#xff0c;大学生需要认清当下的就业环境&#xff0c;并做出相应的应对策略。 了解行业趋势&#xff0c;抓住就业机会 如今&#xff0c;各行各业的竞争日益激烈&#xff…

YOLO目标检测——水果蔬菜数据集下载分享

水果蔬菜数据集共同90000图片&#xff0c;131类别分别存放在不同文件中&#xff0c;可应用于果蔬分类和种类识别等等 数据集点击下载&#xff1a;YOLO水果蔬菜数据集90000图片131类别.rar

Ubuntu 启动出现grub rescue

​ 一&#xff0c;原因 原因&#xff1a;出现 “grub rescue” 错误通常表示您的计算机无法正常引导到操作系统&#xff0c;而是进入了 GRUB&#xff08;Grand Unified Bootloader&#xff09;紧急模式。这可能是由于引导加载程序配置错误、硬盘驱动器损坏或其他引导问题引起…

【Web系列二十四】使用JPA简化持久层接口开发

目录 环境配置 1、引入依赖 配置文件 代码编写 实体类创建 JPA常用注解 Service与ServiceImpl Service ServiceImpl Controller Dao 三种实现Dao功能方式 1.继承接口&#xff0c;使用默认接口实现 2.根据接口命名规则默认生成实现 3.自定义接口实现(类似MyBatis…

洋河稳中求进,上半年营收218.72亿元,展现行业龙头的发展实力与定力

8月28日晚&#xff0c;洋河股份公布2023年半年度报告&#xff0c;上半年营业收入218.73亿元&#xff0c;同比增长15.68%&#xff1b;实现归属于上市公司股东的利润78.62亿元&#xff0c;同比增长14.06%。 ​ 面对国际贸易摩擦持续升级、市场需求不断回落、发达经济体持续加息等…