MIT_6.s081_Lab9:Xv6 and File System
于2022年3月8日2022年3月8日由Sukuna发布
Lab 9_1 Large files
在这个实验中,你会拓展文件系统中文件的最大大小,其中一开始文件是12个直接连接块,1个一级索引块,一共16*16 12=268个块,这个时候我们要修改一下改成11个直接相连,1个一级索引块,1个二级索引块,一共256*256 256 11=65536 256 11个块.
其中xv6把文件系统映射到fs.img中,这个一共有二十万个块,其中70个保存块信息的meta块,剩下的都是数据.完成这个实验你需要关注磁盘 inode 的格式,这个由 fs.h 中的 struct dinode 定义。 您对 NDIRECT、NINDIRECT、MAXFILE 和结构 dinode 的 addrs[] 元素特别感兴趣。 查看 xv6 文本中的图 8.3 以了解标准 xv6 inode 的图表。 在磁盘上查找文件数据的代码在 fs.c 的 bmap() 中。 看看它,确保你明白它在做什么。 bmap() 在读取和写入文件时都会被调用。 写入时,bmap() 会根据需要分配新块来保存文件内容,并在需要时分配间接块来保存块地址。 bmap() 处理两种块号。 bn 参数是一个“逻辑块号”——文件中的块号,相对于文件的开头。 ip->addrs[] 中的块号和 bread() 的参数是磁盘块号。 您可以将 bmap() 视为将文件的逻辑块号映射到磁盘块号。
修改 bmap() ,使其除了直接块和单间接块外,还实现双重间接块。 你只需要 11 个直接块,而不是 12 个,就可以为新的双重间接块腾出空间; 您不能更改磁盘 inode 的大小。 ip->addrs[] 的前 11 个元素应该是直接块; 第 12 个应该是一个单独的间接块(就像现在的块一样); 第 13 个应该是你新的双重间接块。
1) 修改直接映射的数量.
代码语言:javascript复制#define NDIRECT 11#define NDIRECT 11
uint addrs[NDIRECT 2];
#define MAXFILE (NDIRECT NINDIRECT NDOUBLE)//文件最大值也要改变
其中直接映射的数量由原来的12个改为11个.
2) 对应的file.h的inode结构体也要进行更改
代码语言:javascript复制struct inode {
uint dev; // Device number
uint inum; // Inode number
int ref; // Reference count
struct sleeplock lock; // protects everything below here
int valid; // inode has been read from disk?
short type; // copy of disk inode
short major;
short minor;
short nlink;
uint size;
uint addrs[NDIRECT 2];
};
3) 阅读bmap的代码: 0-NDIRECT-1 :直接 ,NDIRECT,NDIRECT NINDIRECT-1:一级间接
代码语言:javascript复制//bn:the number the blocks.
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;
}
有个大前提,就是如果索引为0就代表这个磁盘为空,需要申请一个返回一个磁盘块号
首先第一部分,就是直接映射的部分,如果寻找的逻辑地址是在NDIRECT以内的,就可以直接访问磁盘块.
第二部分就是一级间接映射,首先先把一级映射的映射表找到,读出来,如果没有映射表就新建一个.映射表我们之前在操作系统学过就是一个int类型数组,这个时候我们就可以把映射表解释称int类型数组,然后根据索引值,也就是bn-NDIRECT来寻找,寻找就是寻找数组里面的东西.
4) 依葫芦画瓢作出二级索引.
代码语言:javascript复制 bn -= NINDIRECT;
if(bn < NDOUBLE){
if((addr = ip->addrs[NDIRECT 1]) == 0)
ip->addrs[NDIRECT 1] = addr = balloc(ip->dev);
bp = bread(ip->dev, addr);
a = (uint*)bp->data;
int bn1 = bn / NINDIRECT,bn2 = bn % NINDIRECT;
//first suoyin
if((addr = a[bn1]) == 0){
a[bn1] = addr = balloc(ip->dev);
log_write(bp);
}
//second suoyin
brelse(bp);
bp=bread(ip->dev,addr);
a=(uint*)bp->data;
if((addr=a[bn2])==0)
{
a[bn2]=addr=balloc(ip->dev);
log_write(bp);
}
brelse(bp);
return addr;
}
二级索引需要两个索引值,一个是除得来的结果,一个是%得来的结果,首先先读一次,获得一级索引表,做法和上面是一样的,接着再读,根据一级索引表得来的结果就是对应二级索引表的位置,所以说根据一级索引表的结果打开二级索引表,找到最后属于我们的索引值.
5) 完成索引的释放.
代码语言:javascript复制 if(ip->addrs[NDIRECT 1]) {
bp = bread(ip->dev, ip->addrs[NDIRECT 1]);
a = (uint*)bp->data;
for(j = 0; j < NINDIRECT; j ) {
if(a[j]) {
bp2 = bread(ip->dev, a[j]);
a2 = (uint*)bp2->data;
for(i = 0; i < NINDIRECT; i ) {
if(a2[i]) bfree(ip->dev, a2[i]);
}
brelse(bp2);
bfree(ip->dev, a[j]);
a[j] = 0;
}
}
brelse(bp);
bfree(ip->dev, ip->addrs[NDIRECT 1]);
ip->addrs[NDIRECT] = 0;
}
就是先读取数组的第13项,构造一个二重循环,先进入到第一个一级索引的第一个二级索引陆续地释放所有的块.再进入到第一个一级索引的第二个二级索引…第一个一级索引的第256个二级索引….第256个二级索引的第256个一级索引…
Lab9_2 Symbolic links
您将实现 symlink(char *target, char *path) 系统调用,它会在 path 处创建一个新的符号链接,该链接引用由 target 命名的文件。
首先,为symlink创建一个新的系统调用号,在user/usys.pl,user/user.h中添加一个入口,在kernel/sysfile.c中实现一个空的sys_symlink。 将新文件类型 (T_SYMLINK) 添加到 kernel/stat.h 以表示符号链接。 向 kernel/fcntl.h 添加一个新标志 (O_NOFOLLOW),可与 open 系统调用一起使用。请注意,传递给 open 的标志是使用按位 OR 运算符组合的,因此您的新标志不应与任何现有标志重叠。这将让您在将 user/symlinktest.c 添加到 Makefile 后编译它。 实现 symlink(target, path) 系统调用以在指向目标的路径上创建一个新的符号链接。请注意,系统调用成功时不需要存在目标。您将需要选择某个位置来存储符号链接的目标路径,例如,在 inode 的数据块中。 symlink 应该返回一个表示成功 (0) 或失败 (-1) 的整数,类似于链接和取消链接。 修改 open 系统调用以处理路径引用符号链接的情况。如果文件不存在,则打开必须失败。当进程在要打开的标志中指定 O_NOFOLLOW 时, open 应该打开符号链接(而不是跟随符号链接)。 如果链接文件也是符号链接,则必须递归地跟随它,直到到达非链接文件。如果链接形成循环,则必须返回错误代码。如果链接的深度达到某个阈值(例如,10),您可以通过返回错误代码来近似此值。 其他系统调用(例如,链接和取消链接)不得遵循符号链接;这些系统调用对符号链接本身进行操作。 对于本实验,您不必处理指向目录的符号链接。
1) 添加系统调用.(略)
2) 按照实验提示的信息,添加两个新的宏.(略)
3) 完成symlink系统调用.
首先第一点,我们发现,在sysfile.c的系统调用是需要提交事务的,我们也模仿这一点进行更改,处理的思路就是获取参数,创建一个新的inode,把符号连接的文件路径写进inode里面,然后提交即可.
代码语言:javascript复制uint64
sys_symlink(void)
{
char path[MAXPATH], target[MAXPATH];
struct inode *ip;
// get parameter
if(argstr(0, target, MAXPATH) < 0)
return -1;
if(argstr(1, path, MAXPATH) < 0)
return -1;
begin_op();
// create a inode
if((ip = create(path, T_SYMLINK, 0, 0)) == 0) {
end_op();
return -1;
}
// write the path of the file to the inode.
if(writei(ip, 0, (uint64)target, 0, MAXPATH) < MAXPATH) {
iunlockput(ip);
end_op();
return -1;
}
iunlockput(ip);
end_op();
return 0;
}