mit6s081 lab9 file system - 实践
mit6s081 lab9 file system
一、Large Files
题目描述
目前 xv6 系统文件大小限制为 268 个块,因为 inode 中包含:
- 12 个直接区块号
- 1 个单间接区块号(singly indirect)
单间接块号最多容纳 256 个块(1024 / 4 = 256),因此最大块数为:
12 + 256 = 268
现在希望能够创建 65803 个块大小的文件。
为此,需要修改文件系统,使 inode 支持 双间接块(doubly indirect),即三级索引:
11 + 256 + 256*256 = 65803 块
任务:修改 bmap() 以支持双间接块。
在 ip->addrs[] 中:
- 前 11 个元素是直接块;
- 第 12 个是单间接块;
- 第 13 个是双间接块。
完成后运行 bigfile 和 usertests 验证功能。
实现步骤
1️⃣ 查看文件系统信息
make fs.img | grep "nmeta"

2️⃣ 定义 inode 索引结构
#define NDIRECT 11
#define NINDIRECT (BSIZE / sizeof(uint))
#define N2INDIRECT (BSIZE / sizeof(uint))*(BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + NINDIRECT + N2INDIRECT)
修改 bmap() 实现双间接索引
static uint
bmap(struct inode *ip, uint bn)
{
uint addr, *a, *a1;
struct buf *bp;
struct buf *bp1;
struct buf *bp2;
uint *a2;
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) // ==0代表没有间接块,需要分配
ip->addrs[NDIRECT] = addr = balloc(ip->dev); //先右后左
bp = bread(ip->dev, addr); //读取间接块
a = (uint*)bp->data; // 把bp里面的内容转换为uint*,然后可以用数组形式访问
if((addr = a[bn]) == 0){ // ==0代表没有数据块,需要分配
a[bn] = addr = balloc(ip->dev);
log_write(bp); // 只是把bp写到log中,并没有写到磁盘
}
brelse(bp);
return addr;
}
bn -= NINDIRECT;
if(bn < N2INDIRECT){
if((addr = ip->addrs[NDIRECT+NINDIRECT]) == 0)
ip->addrs[NDIRECT+NINDIRECT] = addr = balloc(ip->dev);
bp1 = bread(ip->dev, addr); //读取间接块
a1 = (uint*)bp1->data; // 把bp1里面的内容转换为uint*,然后可以用数组形式访问
if((addr = a1[bn/NINDIRECT]) == 0){
a1[bn/NINDIRECT] = addr = balloc(ip->dev);
log_write(bp1);
}
brelse(bp1);
bp2 = bread(ip->dev, addr);
a2 = (uint*)bp2->data;
if((addr = a2[bn%NINDIRECT]) == 0){
a2[bn%NINDIRECT] = addr = balloc(ip->dev);
log_write(bp2);
}
brelse(bp2);
return addr;
}
panic("bmap: out of range");
}
修改 itrunc() 实现清空双重间接块
这个函数相当于把一个文件内容清零
// Truncate inode (discard contents).
// Caller must hold ip->lock.
void
itrunc(struct inode *ip)
{
int i, j;
struct buf *bp;
struct buf *bp1;
struct buf *bp2;
uint *a1;
uint *a2;
uint *a;
for(i = 0; i < NDIRECT; i++){
if(ip->addrs[i]){
bfree(ip->dev, ip->addrs[i]);
ip->addrs[i] = 0;
}
}
if(ip->addrs[NDIRECT]){
bp = bread(ip->dev, ip->addrs[NDIRECT]);
a = (uint*)bp->data;
for(j = 0; j < NINDIRECT; j++){
if(a[j])
bfree(ip->dev, a[j]);
}
brelse(bp);
bfree(ip->dev, ip->addrs[NDIRECT]);
ip->addrs[NDIRECT] = 0;
}
if(ip->addrs[NDIRECT+1]){
bp1 = bread(ip->dev, ip->addrs[NDIRECT+1]);
a1 = (uint*)bp1->data;
for(j = 0; j < NINDIRECT; j++){
bp2 = bread(ip->dev, a1[j]);
a2 = (uint*)bp2->data;
for(int k = 0; k < NINDIRECT; k++){
if(a2[k])
bfree(ip->dev, a2[k]);
a2[k] = 0;
}
bfree(ip->dev, a1[j]);
a1[j] = 0;
brelse(bp2);
}
brelse(bp1);
bfree(ip->dev, ip->addrs[NDIRECT+1]);
brelse(bp);
bfree(ip->dev, ip->addrs[NDIRECT+NINDIRECT]);
ip->addrs[NDIRECT+NINDIRECT] = 0;
}
ip->size = 0;
iupdate(ip);
}
修改 inode 结构体定义
kernel/fs.h
// kernel/fs.h
// On-disk inode structure
struct dinode {
short type; // File type
short major; // Major device number (T_DEVICE only)
short minor; // Minor device number (T_DEVICE only)
short nlink; // Number of links to inode in file system
uint size; // Size of file (bytes)
uint addrs[NDIRECT+2]; // Data block addresses
};
kernel/file.h
// kernel/file.h
// in-memory copy of an inode
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];
};
✅ 测试结果
运行:
make qemu
如果报错,删除旧的 fs.img 再重新生成。
运行 bigfile 和 usertests 均通过,说明实现成功。

二、Symbolic Links(符号链接)
题目描述
向 xv6 添加 符号链接(symbolic link) 功能。
当打开符号链接时,内核根据路径名找到被引用的文件。
⚙️ 符号链接与硬链接不同:
- 硬链接指向同一磁盘文件;
- 符号链接文件内容是目标路径字符串,可以跨设备。
实现步骤
1️⃣ 定义符号链接类型与标志位
kernel/stat.h:
#define T_SYMLINK 4 // Symlink
kernel/fcntl.h:
#define O_RDONLY 0x000
#define O_WRONLY 0x001
#define O_RDWR 0x002
#define O_CREATE 0x200
#define O_TRUNC 0x400
#define O_NOFOLLOW 0x800
2️⃣ 实现系统调用 symlink(target, path)
uint64
sys_symlink(void)
{
char path[MAXPATH];
char target[MAXPATH];
struct inode *ip;
// 用户接口:symlink(target, path)
if(argstr(0, target, MAXPATH) < 0 || argstr(1, path, MAXPATH) < 0)
return -1;
begin_op();
if((ip = create(path, T_SYMLINK, 0, 0)) == 0){ // 分配一个符号链接类型的inode
end_op();
return -1;
}
// 向符号链接的inode中写入目标路径(按实际长度)
if(writei(ip, 0, (uint64)target, 0, strlen(target)) != strlen(target)){
iunlockput(ip); // 写入不成功则释放inode
end_op();
return -1;
}
iunlockput(ip);// 使用完毕应该解锁并对inode引用数(nlink代表该inode被使用数)-1;
end_op();
return 0;
}
3️⃣ 修改 sys_open() 支持符号链接解析
在path处创建一个链接文件,文件内容是target这个字符串本身,而不是将target解析为绝对路径在存储。
uint64
sys_open(void)
{
char path[MAXPATH];
int fd, omode;
struct file *f;
struct inode *ip;
int n;
if((n = argstr(0, path, MAXPATH)) < 0 || argint(1, &omode) < 0)
return -1;
begin_op();
if(omode & O_CREATE){ // 创建文件
ip = create(path, T_FILE, 0, 0);
if(ip == 0){
end_op();
return -1;
}
} else { // 打开文件
if((ip = namei(path)) == 0){
end_op();
return -1;
}
ilock(ip);
if(ip->type == T_DIR && omode != O_RDONLY){ // 打开目录,但是不是只读模式,返回错误
iunlockput(ip);
end_op();
return -1;
}
}
// 符号链接处理:根据 O_NOFOLLOW 决定是否解析,解析时正确加锁/解锁并限制深度
if(ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)){
int max_depth = 10;
while(ip->type == T_SYMLINK){
char linkpath[MAXPATH];
int len = ip->size;
if(len >= MAXPATH) len = MAXPATH - 1;
if(len < 0){
iunlockput(ip);
end_op();
return -1;
}
if(len > 0){
if(readi(ip, 0, (uint64)linkpath, 0, len) != len){
iunlockput(ip);
end_op();
return -1;
}
}
linkpath[len] = '\0';
iunlockput(ip);
ip = namei(linkpath);
if(ip == 0){
end_op();
return -1; // 目标不存在
}
ilock(ip);
if(--max_depth <= 0){
iunlockput(ip);
end_op();
return -1; // 循环或过深
}
}
// 解析完后若是目录且非只读,禁止
if(ip->type == T_DIR && omode != O_RDONLY){
iunlockput(ip);
end_op();
return -1;
}
}
if(ip->type == T_DEVICE && (ip->major < 0 || ip->major >= NDEV)){ // 设备号非法
iunlockput(ip);
end_op();
return -1;
}
if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){ // 分配文件描述符
if(f)
fileclose(f);
iunlockput(ip);
end_op();
return -1;
}
if(ip->type == T_DEVICE){ // 设备文件
f->type = FD_DEVICE;
f->major = ip->major;
} else {
f->type = FD_INODE;
f->off = 0;
}
f->ip = ip;
f->readable = !(omode & O_WRONLY);
f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
if((omode & O_TRUNC) && ip->type == T_FILE){ // 如果打开文件并指定O_TRUNC,则清空文件内容
itrunc(ip);
}
iunlock(ip);
end_op();
return fd;
}
✅ 测试结果
运行:
make symlinktest
结果如下图所示:

实验总结
| 实验 | 核心修改点 | 主要函数 |
|---|---|---|
| Large files | inode 增加双重间接块索引 | bmap()、itrunc() |
| Symbolic links | 实现符号链接系统调用及解析 | sys_symlink()、sys_open() |
要点回顾:
- 实验一:通过增加双间接块扩展文件系统可支持文件大小;
make symlinktest
结果如下图所示:
[外链图片转存中…(img-DqbgBAMh-1760455284134)]
实验总结
| 实验 | 核心修改点 | 主要函数 |
|---|---|---|
| Large files | inode 增加双重间接块索引 | bmap()、itrunc() |
| Symbolic links | 实现符号链接系统调用及解析 | sys_symlink()、sys_open() |
要点回顾:
- 实验一:通过增加双间接块扩展文件系统可支持文件大小;
- 实验二:实现软链接,使文件系统支持路径引用。
浙公网安备 33010602011771号