lab9 fs

这篇具有很好参考价值的文章主要介绍了lab9 fs。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

lab9 fs,6.S081,linux

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;
    // 创建软链接文件的inode
    begin_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说简单也简单,说难也难,主要是我人菜还不愿意慢慢学文章来源地址https://www.toymoban.com/news/detail-681939.html

到了这里,关于lab9 fs的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • MIT6.S081 - Lab2: system calls

    step1:系统调用声明 user/user.h :系统调用函数(如 int fork(void) ) step2:ecall 进入内核态 user/usys.S (该文件由 user/usys.pl 生成,后续添加函数可以在这里添加):执行如下命令 将系统调用的编号(在 kernel/syscall.h 中定义)写入 a7 寄存器 从 ecall 进入中断处理函数 step3:保存数据并

    2024年04月23日
    浏览(9)
  • 6.s081/6.1810(Fall 2022)Lab2: System calls

    6.s081/6.1810(Fall 2022)Lab2: System calls

    这个lab主要介绍了用户态到内核态的系统调用做了什么,并让我们照猫画虎完成了两个系统调用的实现。 环境搭建 Lab1: Utilities Lab2: System calls Lab3: Page tables Lab4: Traps Lab5: Copy-on-Write Fork for xv6 官网链接 xv6手册链接,这个挺重要的,建议做lab之前最好读一读。 xv6手册中文版,这

    2024年02月13日
    浏览(30)
  • MIT 6s081 lab1:Xv6 and Unix utilities

    MIT 6s081 lab1:Xv6 and Unix utilities

    作业网址:https://pdos.csail.mit.edu/6.828/2020/labs/util.html 下载,启动xv6系统 使用system call sleep .函数(在user/user.h中被声明)来完成 使用系统调用在一对管道上的两个进程之间“乒乓”一个字节,每个方向一个。父进程应该向子进程发送一个字节;子进程应打印“<pid>:received

    2024年01月17日
    浏览(9)
  • MIT6.S081 - Lab1: Xv6 and Unix utilities

    可以参考 user/echo.c , user/grep.c 和 user/rm.c 文件 如果用户忘记传递参数, sleep 应该打印一条错误消息 命令行参数传递时为字符串,可以使用 atoi 函数将字符串转为数字 使用系统调用 sleep ,有关实现 sleep 系统调用的内核代码参考 kernel/sysproc.c (查找 sys_sleep ),关于可以从用户程序

    2024年04月16日
    浏览(14)
  • 6.s081/6.1810(Fall 2022)Lab5: Copy-on-Write Fork for xv6

    6.s081/6.1810(Fall 2022)Lab5: Copy-on-Write Fork for xv6

    本来往年这里还有个Lazy Allocation的,今年不知道为啥直接给跳过去了。. 环境搭建 Lab1: Utilities Lab2: System calls Lab3: Page tables Lab4: Traps Lab5: Copy-on-Write Fork for xv6 官网链接 xv6手册链接,这个挺重要的,建议做lab之前最好读一读。 xv6手册中文版,这是几位先辈们的辛勤奉献来的呀!

    2024年02月14日
    浏览(28)
  • Linux: FS: 执行文件出现13 EACCES

    https://blog.csdn.net/qq_36428903/article/details/133098586 这里需要注意的一点是,检查文件的可执行权限的顺序是: 看文件的权限; 看对应的shell命令是否有权限执行; 看文件所属的目录的权限;这一点也是比检查的项;

    2024年02月16日
    浏览(42)
  • Linux_红帽8学习笔记分享_8(文件系统管理FS Management)

    Linux_红帽8学习笔记分享_8(文件系统管理FS Management)

    它的全称是Basic Input/Output System即基本输入/输出系统,是比较老的传统的操作系统启动方式,如果笔记本电脑是win7之前的,红帽企业6之前的,那么它采用的启动方式都是BIOS启动方式。它在开机时需要进行自检,启动过程较复杂,时间较长。它无法识别GPT分区表,只能识别MBR分区

    2024年02月02日
    浏览(8)
  • 阿里云Linux热扩容云盘(growpart和resize2fs工具)

    阿里云Linux热扩容云盘(growpart和resize2fs工具)

    阿里云linux机器系统盘空间不够进行扩容 阿里云控制台在线扩容完成 1、检查云盘大小 /dev/vda1显示容量为20G(在线扩容后的容量,扩容部分只是增加了物理容量,分区和文件系统还不能使用) 2、运行df -h命令查看云盘分区大小。 以下示例返回分区(/dev/vda1)容量是20G 3、文件系统

    2024年02月02日
    浏览(15)
  • Linux修改fs.inotify.max_user_watches(“外部文件更改同步可能很慢”和“当前的 inotify(7) 监视限制太低”)

    fs.inotify.max_user_watches 参数是用于控制 Linux 内核中 inotify 子系统的观察者数量限制。inotify 是一种文件系统监控机制,它可以用于检测文件或目录的变化,并在事件发生时通知相关的应用程序。 具体而言, fs.inotify.max_user_watches 参数限制了每个用户所能创建的 inotify 实例的数量

    2024年02月06日
    浏览(10)
  • B081-Lucene+ElasticSearch

    B081-Lucene+ElasticSearch

    认识全文检索 概念 对非结构化数据的搜索就叫全文检索,狭义的理解主要针对文本数据的搜索。 非结构化数据: 没有固定模式的数据,如WORD、PDF、PPT、EXL,各种格式的图片、视频等。 非结构化数据是数据结构不规则或不完整,没有预定义的数据模型,不方便用数据库二

    2024年02月10日
    浏览(14)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包