
文章目录
- Large files
- task
- hints
- 思路
 
- Symbolic links
- task
- hints
- 思路
- sys_symlink
- sys_open
 
 
Large files
目标:11+256+256*256个block
inode的格式在fs.h的struct dinode中被定义,你需要特别注意以下几点
- NDIRECT
- NINDIRECT
- MAXFILE
- addrs[]
在磁盘上找一个文件数据是通过fs.c中的bmap()实现的
- 无论是读还是写文件,都调用了bmap
- 在写文件时,bmap()分配了新的block去容纳文件内容,在必要的时候,会去分配一个非直接映射块
bmap处理了两种块号
- bn参数是一个逻辑块号,是在一个文件中相对于文件起始位置的块号
- 而ip->addrs[]和bread()的参数中的块号,都是磁盘块号
- 你可以将bmap看做是逻辑块号到物理块号的映射
task
- 修改bmap使其通过addrs[]支持11+256+256*256个磁盘块
- 如果能通过bigfile和usertests测试,就说明成功
hints
- 保证你理解了bmap()。画图理清楚inode中不同类型的块的指向和作用
- 想一下你如何通过逻辑块号索引一级地址块和直接地址块
- 如果你改变了NDIRECT,你可能需要去改变file.h中struct inode中的addrs[]的声明。保证struct inode和struct dinode在addrs数组中有相同数量的元素
- 如果你改变了NDIRECT的定义,保证你创造了一个新的fs.img,即make clean 然后make qemu
- 对任何一个block进行bread之后都要记得brelse
- 你应该只在必要的时候分配一级地址和二级地址
- 保证itrunc将一个文件所有的block都free了,包括新增的二级地址
思路
文件系统这一块,感觉学的很难,各种函数很多,但是这个task这一块是不太难,不过我也做了好久。。。
这个task只需要修改bmap和itrunc两个函数,以及一些宏常量,之所以只修改这么点东西就可以给一个文件扩容,应该是因为其他函数都是通过bmap来获取逻辑块对应的物理块号的,它们只负责要和写,根本不管到底使用了多少block
首先,需要修改一些宏常量,并且将dinode和inode的addrs数组长度修改
#define NDIRECT 11
#define NINDIRECT (BSIZE / sizeof(uint))
#define N2INDIRECT (NINDIRECT * NINDIRECT)
#define MAXFILE (NDIRECT + NINDIRECT + N2INDIRECT)
uint addrs[NDIRECT + 2];
然后,修改bitmap函数,首先可以看一下bitmap如何处理直接地址和一级地址,学习一下基本的思路,我们这里基本就是嵌套一下一级地址的情况。
具体实现如下:
-  首先,将逻辑块号减去一级地址的块数 
-  然后这里使用了一个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
- 增加一个系统调用symlink(char *target, char *path)
- 需要通过symlinktest,usertests
hints
-  增加系统调用的流程 - Makefile,加入的不是symlink,而是symlinktest
- user/usys.pl
- user/user.h
- kernel/sysfile.c
- syscall.h && syscall.c
 
- Makefile,加入的不是
-  在 kernel/stat.h中增加一个新的文件类型T_SYMLINK代表软链接
-  在 kernel/fcntl.h中增加一个标志位O_NOFOLLOW,因为open文件的标志位是or起来的,因此你不能和已有的发生重叠
-  你需要找一个位置去存储软链接的目标地址,例如在 inode数据块symlink应该返回0表示成功,返回-1表示失败
-  修改 open系统调用去处理一个路径指向软链接的情况如果文件不存在,open必须失败 当一个进程在open中指定了 O_NOFOLLOW,则说明不是打开target,而是打开软链接
-  如果被链接的文件也是一个软链接,你必须递归地访问,直到访问一个正确的文件 你可以定义最大递归的层数,比如10 
-  Other system calls (e.g., link and unlink) must not follow symbolic links; these system calls operate on the symbolic link itself. 
-  你不需要处理软链接到目录的情况 
思路
这玩意看着很抽象,但是其实搞清楚以下几件事就行了
-  访问文件就是先访问得到inode,然后通过inode去写对应的文件 通过readi就可以读取path对应的inode 通过writei就可以在inode对应的文件中去写 
-  软链接的作用 在open它的时候,它会直接导向target 
-  创建软链接,分为以下几步 - 首先创建一个文件,即获得一个inode,这个可以通过create函数实现
- 将我们的target写入这个inode,我们就将target存在第一个文件数据块就行了
 
-  打开软链接对应的文件,分为以下几步 - 在open中获取软链接对应的真实的inode
- 然后就让open对这个inode进行分配fd和file的操作即可
 
代码很少,但是思路真的很有意思
sys_symlink
这里可以先看看create和open的代码是如何使用xv6提供的一些api的,主要是
create,writei,readi
- 首先,我们需要将target和path这两个参数从寄存器中读出来,使用argstr即可
- 然后,我们需要创建inode,使用create函数,第一个参数是软链接的路径,第二个参数是文件类型,我们这里当然是新建的那个,后面两个参数不知道啥意思,模仿其他函数的使用,填0
- 将target写入软链接文件,也就是写入数据块,使用writei函数 
  - 第一个参数是inode的指针
- 第三个参数是我们写入的东西的地址,这里就是target的地址
- 第四个参数是写到文件的哪里,其实就是使用一个偏移量完成,我们软链接文件没其他的文件内容,就写到偏移量为0的地方,也就是文件的起始位置
- 最后一个参数是写入多少个字节
- 注意,这里如果操作失败了,需要将这个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_op和end_op,比如create之后是会自动给inode上锁的
sys_open
这一个函数的修改就是对应我们真正使用软链接的情况
如果我们设置了O_NOFOLLOW,那说明不是访问target,就是想访问这个软链接,那就正常open就行了
而如果我们没设置,说明实际上要访问的是target,在这种情况下,我们只需要在open函数分配fd和file之前,将ip指针切换成target的ip地址即可,因此,找一个适当的位置截胡即可。我这里选择的是在获取已有文件的inode时进行的
- 首先,如果进入了else分支,都进入这个while循环,这个while循环走来就读取path的inode,如果不是软链接或者不是需要target的情况,那就直接break,这样的话就和之前的open一样了
- 如果需要找target,那就会读出当前软链接文件的target,然后解锁当前inode,进入下一轮while循环,获取target的inode,如果还是软链接,则递归操作,这里是通过迭代代替递归 
  - 这中间关键的函数是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说简单也简单,说难也难,主要是我人菜还不愿意慢慢学



















![[ES]二基础 |](https://img-blog.csdnimg.cn/0c67af8dc14c4f11a67ca27d370734a7.png)