lab9 fs

image-20230829122900742

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说简单也简单,说难也难,主要是我人菜还不愿意慢慢学

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值