#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
行通过判断迭代次数,来判断软链接是否成环,如果软链接成环,也应返回错误。