6.S081 lab9 file system
#Large files
第一个实验还算比较简单的,不过 测试样例好像不是很严格,刚开始我做完后拿去跑测试样例,过了,但其实代码是有点小问题的,然后这些问题就导致了我第二个实验
symlink的部分样例过不了,我花了大量时间debug第二个实验的代码,结果最后发现是第一个实验的代码有问题。
-
bmap函数有两个形参(ip和bn),ip是一个inode指针,而bn是逻辑块号。bmap函数的作用是根据逻辑块号,返回在磁盘中物理块号的地址。inode中有一个成员addrs,这个成员记录了NDIRECT个直接块的地址(addrs[0]~addrs[NDIRECT-1])和1个一级索引块的地址(addrs[NDIRECT]) -
先把思路捋清楚,实验是要求我们增加一个二级索引块,我们要做的就是改
bmap函数和itrunc函数。减少一个直接数据块的数目为二级索引块腾出空间,修改宏定义如下图:
修改
inode和dinode中addrs的定义,修改后的addrs[NIDRECT+1]为二级索引块
-
修改
bmap函数,增加对于二级索引块的处理代码,注意:bn是逻辑块号,核心代码:// 需要分配二级索引块的情况 bn -= NINDIRECT; if (bn < N2INDIRECT){ // 进行两次查找块的操作(二级索引块->中间块->最终块) // level2是中间块的逻辑块号,level1是最终块的逻辑块号 int level2 = bn / NINDIRECT, level1 = 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[level2]) == 0){ a[level2] = addr = balloc(ip->dev); /* printf("第一次分配 %p\n", addr); */ log_write(bp); } brelse(bp); bp = bread(ip->dev, addr); a = (uint*)bp->data; if ((addr = a[level1]) == 0){ a[level1] = addr = balloc(ip->dev); /* printf("第二次分配 %p\n", addr); */ log_write(bp); } brelse(bp); return addr; } -
修改
itrunc函数,增加对于二级索引块的回收代码。一定要注意:必须回收所有有效块,包括中间块!,核心代码:// 清除二级间接块 if (ip->addrs[NDIRECT + 1]){ bp = bread(ip->dev, ip->addrs[NDIRECT + 1]); a = (uint*)bp->data; for (i = 0; i < NINDIRECT; i++){ // 中间块是否有效? if (a[i]){ bp2 = bread(ip->dev, a[i]); a2 = (uint*)bp2->data; for (j = 0; j < NINDIRECT; j++){ // 最终块是否有效? if (a2[j]) bfree(ip->dev, a2[j]); } // 一定记得回收中间块 brelse(bp2); bfree(ip->dev, a[i]); } } brelse(bp); bfree(ip->dev, ip->addrs[NDIRECT + 1]); ip->addrs[NDIRECT + 1] = 0; }
#Symlink
做这个实验之前,建议看一下与目录相关的代码,这里贴一个链接:https://juejin.cn/post/7002183734178873357
-
软链接是一种特殊的文件,可以把软链接看成windows中的快捷方式,这个文件的内容是一个路径。
-
加入系统调用号,添加函数原型等操作不再赘述。直接阐述
sys_symlink函数的实现方法,这个函数有两个参数:第一个参数是target,表示链接的目标路径,简单来说就是快捷方式指向的文件,第二个参数是path,这个参数指定了链接的存储路径。举个例子,target="/a/b", path="/a/c"就代表在目录a下存在软链接c,此软链接指向了文件b。实现代码如下:uint64 sys_symlink(void){ char target[MAXPATH], path[MAXPATH]; struct inode *ip; if (argstr(0, target, MAXPATH) < 0 || argstr(1, path, MAXPATH) < 0) return -1; begin_op(); ip = create(path, T_SYMLINK, 0, 0); if (ip == 0){ end_op(); return -1; } if (writei(ip, 0, (uint64)target, 0, MAXPATH) != MAXPATH) panic("error writei!"); iunlockput(ip); end_op(); return 0; }介绍一下所用的
xv6 API,第10行的create函数以文件类型T_SYMLINK在路径path处分配了一个inode,如果分配失败则返回0,否则返回一个inode指针。分配了一个新inode之后,应该把target写入这个inode中,writei的工作就是在于此,具体writei的使用方法可以参看详细代码,这里就不过多说明了。需要注意的一点是:create函数返回的是一个上锁的inode,所以最后需要iunlockput函数解锁inode。 -
之后需要修改
sys_open函数来处理路径指向软链接的情况。核心代码如下:// 处理软链接文件 int cnt = 0; // 迭代次数 while(ip->type == T_SYMLINK && !(omode & O_NOFOLLOW) && cnt < 10){ if (readi(ip, 0, (uint64)path, 0, MAXPATH) != MAXPATH) panic("readi error"); cnt++; iunlockput(ip); ip = namei(path); if (ip == 0){ end_op(); return -1; } ilock(ip); } // 若迭代次数已达到10次,就认为软链接形成了环状 if (cnt == 10){ iunlockput(ip); end_op(); return -1; }当
O_NOFOLLOW标记为0并且文件类型为T_SYMLINK时,应该迭代进行查找。查找逻辑比较简单:第4行的readi函数与writei函数类似,只不过readi函数是从一个inode读数据到path中。读到软链接所指向的文件后,应该查找此文件所在的inode,第8行的namei函数的目的即在于此,给出路径名path,查找path所在的inode,若查找失败则返回0,此时说明软链接指向的文件不存在,应该返回错误。在迭代过程要特别注意:第7行和第13行,由于第8行需要更新ip,则旧的inode不会再用到了,需要释放。每次读写inode之前,一定要获取inode的锁。代码的
16~20行通过判断迭代次数,来判断软链接是否成环,如果软链接成环,也应返回错误。

浙公网安备 33010602011771号