xv6 6.S081 Lab8: fs

news/2024/10/30 9:34:04/

xv6 6.S081 Lab8: fs

    • 写在前面
    • 实验介绍
    • 开始!
      • Large File
      • Symbolic links

fs代码在这里。我的妈呀,终于要写完了,xv6的file system考察难度并不大,这里强烈推荐我工Ext2 Based File System,这里可以给一下参考代码与参考结果,后面找机会写写博客。

写在前面

xv6中的file system结构与Ext 2的结构类似,如下图所示:
在这里插入图片描述

在完成本实验前,推荐阅读xv6 book Chapter 7.9,搞清楚inode的索引结构。

与往常一样,在我的博客OS实验xv6 6.S081 开坑中给出了一些有用的参考资料,大家也可以一并参考。

实验介绍

给出本次实验的实验指导书

本次实验主要需要完成两个部分:

  • 为inode添加一个二级索引,使之能够支持分配更大的文件;
  • 学会如何创建symbol link软链接

开始!

Large File

在这里插入图片描述

首先要理解inode的结构是什么样的,这里的addrs数组即ext2 file system中的索引指针。根据官方指导书的说法,xv6中的inode有12个直接索引,1个一级索引。另外xv6的数据块大小为1024B,索引指针大小为4B,因此一个数据块可以装256个索引指针,因此,目前一个inode最大支持12 + 256 = 268个数据块。需要注意的是,在xv6中有两种类型的inode,其中一个是存在磁盘中的inode,另一种是缓存在内存中的inode,在对inode结构进行修改的时候需要修改这两处。inode的结构如下:

// On-memory inode structure
struct inode {uint dev;           // Device numberuint inum;          // Inode numberint ref;            // Reference countstruct sleeplock lock; // protects everything below hereint valid;          // inode has been read from disk?short type;         // copy of disk inodeshort major;short minor;short nlink;uint size;uint addrs[NDIRECT+1];
};// 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)/** * The nlink field counts the number of directory* entries that refer to this inode,* in order to recognize when the on-disk * inode and its data blocks should be freed.   * */short nlink;          // Number of links to inode in file system/*** The size field records the number of bytes of content in the file.*/uint size;            // Size of file (bytes)/*** The addrs array records the block numbers of * the disk blocks holding the file’s content.*/uint addrs[NDIRECT+1];   // Data block addresses
};

下图给出了一个基本的inode结构示意图:
在这里插入图片描述
本次任务即为xv6中的inode添加一个Doubel Indirect索引块,这样每一个inode就能够支持11 + 256 + 256 * 256 = 65803个数据块的文件了。实现并不困难,我们首先修改宏定义如下:

/** direct blocks */
#define NDIRECT 11
/** indirect blocks */
#define SINGLEDIRECT (BSIZE / sizeof(uint))
#define NINDIRECT (SINGLEDIRECT + SINGLEDIRECT * BSIZE / sizeof(uint))
#define MAXFILE (NDIRECT + NINDIRECT)

其中,NDIRECT代表直接索引指向块的数量;SINGLEDIRECT表示一级索引能够指向的块的数量;NINDIRECT索引代表一级和二级索引一共可指向的块的数量。

其次,根据指导书的Hint:
mkfs initializes the file system to have fewer than 2000 free data blocks, too few to show off the changes you’ll make. Modify kernel/param.h to change FSSIZE from 2000 to 200,000:

#define FSSIZE       200000  // size of file system in blocks

Rebuild mkfs so that is produces a bigger disk: $ rm mkfs/mkfs fs.img; make mkfs/mkfs

我们修改kernel/param.h中的FSSIZE宏定义为200000

好了,接下来开始正式实现二级索引列表,代码实现并不困难,只要搞清楚原来的bmap是如何分配块的就行,在此不做赘述。代码如下:

static uint
bmap(struct inode *ip, uint bn)
{/*** bmap() deals with two kinds of block numbers. * The bn argument is a "logical block number" -- * a block number within the file, relative to the start of the file. * The block numbers in ip->addrs[], and the argument to bread(), * are disk block numbers. * You can view bmap() as * * * mapping a file's logical block numbers into disk block numbers.*/// printf("-----------------------------------------------\n");// printf("bn1: %d\n", bn);// NINDIRECT 256// printf("NINDIRECT: %d\n", NINDIRECT); //256uint addr, *a, *a2;struct buf *bp, *bp2;/** bn是相对file(inode)的虚拟编号  *//** 直接返回块ip->addrs[bn]  */if(bn < NDIRECT){if((addr = ip->addrs[bn]) == 0)ip->addrs[bn] = addr = balloc(ip->dev);return addr;}/** 否则减去NDIRENT  */bn -= NDIRECT;//11 + 256 + 256 * 256if(bn < NINDIRECT){if(bn < SINGLEDIRECT){// Load indirect block, allocating if necessary.if((addr = ip->addrs[NDIRECT]) == 0)ip->addrs[NDIRECT] = addr = balloc(ip->dev);/** Return a locked buf with the contents of the indicated block.  */bp = bread(ip->dev, addr);/*** 为何 #define NINDIRECT (BSIZE / sizeof(uint)) ?* 因为一个buf有BSIZE个data,一个block地址为64位,故刚好存储256个block指针,NINDIRECT = 256*//** 一个文件描述符 */a = (uint*)bp->data; if((addr = a[bn]) == 0){a[bn] = addr = balloc(ip->dev);log_write(bp);}brelse(bp);}else{/** * 实现double-indirect* 结构:* addr -> 256个single-indirect -> 256 * 256个block* 256 + 11 = 267 0 ~ 266为前面的,267开始后面为后面的*  */bn -= SINGLEDIRECT;/** 宏在做乘除运算时记得加括号5555555555555555555  *//** single-indirect索引*/int single_indirect_index = bn / SINGLEDIRECT;/** single-indirect内部相对索引  */   int relative_offset_bn = bn % SINGLEDIRECT;/** 下标12是double-indirect  */int pos = NDIRECT + 1;if((addr = ip->addrs[pos]) == 0)ip->addrs[pos] = addr = balloc(ip->dev);bp = bread(ip->dev, addr);a = (uint*)bp->data;if((addr = a[single_indirect_index]) == 0){printf("bn: %d, addr: %p\n",bn, addr);a[single_indirect_index] = addr = balloc(ip->dev);log_write(bp);}brelse(bp);bp2 = bread(ip->dev, addr);a2 = (uint*)bp2->data; if((addr = a2[relative_offset_bn]) == 0){a2[relative_offset_bn] = addr = balloc(ip->dev);log_write(bp2);}brelse(bp2);}printf("-----------------------------------------------\n");return addr;}panic("bmap: out of range");
}

下面,我们完成itrunc以释放inode下的所有数据块,同样,做法参考原本的itrunc即可,代码如下:

static void
itrunc(struct inode *ip)
{int i, j;struct buf *bp, *bp2;uint *a, *a2;/** Free掉所有Direct */for(i = 0; i < NDIRECT; i++){if(ip->addrs[i]){bfree(ip->dev, ip->addrs[i]);ip->addrs[i] = 0;}}/** Free掉single-direct  */if(ip->addrs[NDIRECT]){bp = bread(ip->dev, ip->addrs[NDIRECT]);a = (uint*)bp->data;/*** 修改NINDIRECT为SINGLEDIRECT* for(j = 0; j < NINDIRECT; j++){*   if(a[j])*     bfree(ip->dev, a[j]);* } */for (j = 0; j < SINGLEDIRECT; j++){/* code */if(a[j])bfree(ip->dev, a[j]);}brelse(bp);bfree(ip->dev, ip->addrs[NDIRECT]);ip->addrs[NDIRECT] = 0;}/** Free掉double-direct  */if(ip->addrs[NDIRECT + 1]){printf("free double\n");int pos = NDIRECT + 1;bp = bread(ip->dev, ip->addrs[pos]);a = (uint*)bp->data;int number_of_single_direct = BSIZE / sizeof(uint);for (i = 0; i < number_of_single_direct; i++){if(a[i]){bp2 = bread(ip->dev, a[i]);a2 = (uint *)bp2->data;for (j = 0; j < SINGLEDIRECT; j++){if(a2[j])bfree(ip->dev, a2[j]);}brelse(bp2);bfree(ip->dev, a[i]);a[i] = 0;}}brelse(bp);bfree(ip->dev, ip->addrs[pos]);ip->addrs[pos] = 0;}ip->size = 0;iupdate(ip);
}

至此,我们完成了Large File部分,测试结果如下:
在这里插入图片描述

Symbolic links

在这里插入图片描述
在完成该实验前,首先需要明白Symbolic Link。可以简单的将它理解为一个存有目标路径的文件,在Windows中可以将其理解为快捷方式。symbolic link文件有两种打开方式,其一是FOLLOW方式,即打开symbolic link文件内容中的路径;其次是NOFOLLOW,即将symbolic link文件视为普通文件,直接打开它。

首先分析Hints:

  • First, create a new system call number for symlink, add an entry to user/usys.pl, user/user.h, and implement an empty sys_symlink.
  • Add a new file type (T_SYMLINK) to kernel/stat.h to represent a symbolic link.
  • Add a new flag to kernel/fcntl.h, (O_NOFOLLOW), that can be used with the open system call. Note that flags passed to open are combined using a bitwise OR operator, so your new flag should not overlap with any existing flags. This will let you compile user/symlinktest.c once you add it to the Makefile.
  • Implement the symlink(target, path) system call to create a new symbolic link at path that refers to target. Note that target does not need to exist for the system call to succeed. You will need to choose somewhere to store the target path of a symbolic link, for example, in the inode’s data blocks.
  • Modify the open system call to handle the case where the path refers to a symbolic link. If the file does not exist, open must fail. When a process specifies O_NOFOLLOW in the flags to open, open should open the symlink (and not follow the symbolic link).
  • If the linked file is also a symbolic link, you must recursively follow it until a non-link file is reached. If the links form a cycle, you must return an error code. You may approximate this by returning an error code if the depth of links reaches some threshold (e.g., 10).
  • Other system calls (e.g., link and unlink) must not follow symbolic links; these system calls operate on the symbolic link itself.
  • You do not have to handle symbolic links to directories for this lab.

逐条解读:

  1. 首先和syscall一样创建sys_symlink系统调用,相关代码如下:

user/usys.pl

entry("symlink");

user/user.h

/** Symbol link */
int symlink(char *target, char *path);

kernel/syscall.h

/** Symbol link  */
#define SYS_symlink 23

kernel/syscall.c

[SYS_symlink] sys_symlink

knerl/sysfile.c

uint64
sys_symlink(void){return 0;
}
  1. kernel/stat.h中添加T_SIMLINK来表示symbolic link文件
  2. kernel/fcntl.h中添加O_NOFOLLOW标志,表示将symbolic link文件视为普通文件打开
  3. 实现symlink(target,path)系统调用来创建一个symbolic link
  4. 修改open函数,使之能够处理T_SIMLINK类型的文件;另外还需要判断标志O_NOFOLLOW已决定是否FOLLOW
  5. 如果在标志不为O_NOFOLLOW的情况下,symbolic link链接的文件还是symbolic link文件,则应该递归寻找直到找到不是symbolic link的文件,将其打开。
  6. 其它系统调用不能FOLLOWsymbolic link 文件
  7. 在本次实验中不用处理指向目录的symbolic link文件

好了,我们可以开始实现了。首先,补充sys_symlink(),使之能够创建一个symbolic link,如何创建文件可以参考sys_open函数中if(omode & O_CREATE)中的内容。需要注意的是,创建后获得该文件的inode,我们应该释放掉该inode下的全部数据块,调用iunlockput即可,否则,会出现inodes不足的错误。 代码及相关注释如下:

uint64
sys_symlink(void){/** 建立软链接文件  */struct inode *ip;int target_len, path_len;char target[MAXPATH], path[MAXPATH];if((target_len = argstr(0, target, MAXPATH)) < 0 || (path_len = argstr(1, path, MAXPATH)) < 0 )return -1;begin_op(ROOTDEV);/*** Create returns a locked inode, but namei does not*/ip = create(path, T_SYMLINK, 0, 0);if(ip == 0){/** 这里记得要加  end_op(ROOTDEV),否则会锁死 */end_op(ROOTDEV);return -1;}if(target_len > MAXPATH)target_len = MAXPATH;memset(ip->target, 0, MAXPATH);memmove(ip->target, target, target_len);/** * iunlock(ip)会报错:没有足够的数据块; * 分配的这个inode可能已经占有了其他数据块,* 而我们只需要利用inode中的target字段保存target,其他的信息我们一概不用,* 因此,我们直接调用iunlockput(ip)将ip的所有dircet、indirect字段全部清理掉* 这样就解决了数据块不够的问题*/iunlockput(ip);end_op(ROOTDEV);return 0;
}

接下来,我们修改sys_open函数,使之能够支持打开symbolic link文件。通过观察sys_open的内容,我们很容易知道可以通过namei函数获取相应路径对应的inode,于是我们便可通过递归的方式实现symbolic link。代码如下:

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(ROOTDEV);if(omode & O_CREATE){ip = create(path, T_FILE, 0, 0);printf("### create %s\n",path);if(ip == 0){end_op(ROOTDEV);return -1;}} else {if((ip = namei(path)) == 0){end_op(ROOTDEV);return -1;}/** Open symlink  */if(ip->type == T_SYMLINK && !(omode & O_NOFOLLOW)){int counter = 0;/** 递归查找  */while ((ip = namei(ip->target)) && ip->type == T_SYMLINK){counter++;if(counter >= 10){printf("open(): too many symlink\n");end_op(ROOTDEV);return -1;}}if(ip == 0){end_op(ROOTDEV);return -1;}}ilock(ip);......
}

最后,make grade测试,成功!
在这里插入图片描述


http://www.ppmy.cn/news/424931.html

相关文章

Walllys/DR6018-S IPQ6010/IPQ6018/IPQ6000

Walllys/DR6018-S IPQ6010/IPQ6018/IPQ6000 Quad-core ARM 64bit A53 1.8GHz Processor Support OpenWRT, Support:QCN9074 WiFi Card Support OpenWRT  IPQ6010  802.11ax  2x2 2.4G&5G  DR6018-S IPQ6010/IPQ6018/IPQ6000 Quad-core ARM 64bit A53 1.8GHz Processor…

Android 视频 美颜SDK对比

Android 视频 美颜小结 美颜SDK美颜的本质Android 视频 美颜SDK对比 美颜SDK 蓝色AR 优点&#xff1a;文档规范 技术支持及时 价格适中 缺点&#xff1a;问题较多 包集成偏大 涂图 特点&#xff1a;前YY团队成员&#xff0c;10W 优点&#xff1a; 缺点&#xff1a; 商汤 特点…

YY/T 9706.106-2021医用电气设备 第1-6部分:基本安全和基本性能的通用要求 并列标准:可用性

目录 一、YY/T 9706.106-2021替换YY/T 1474-2016 二、YY/T 9706.106-2021医用电气设备 第1-6部分&#xff1a;基本安全和基本性能的通用要求 并列标准&#xff1a;可用性 IEC60601-1-6:2013 MOD 三、YY/T 1474-2016医疗器械可用性工程对医疗器械的应用&#xff0c;YY/T 1…

INSTALL_FAILED_SHARED_USER_INCOMPATIBLE错误解决

调试程序时项目设置了UID&#xff0c;使用不同的签名出现 Target device: smartisan-yq601-3fa1a5dc Installing APK: /Users/wangliang/workspace/emm-android/build/outputs/apk/emm-android-debug.apk Uploading file to: /data/local/tmp/com.xxx.vvv Installing com.poly…

医疗器械YY0505-2012、YY9706.102-2021检测报告,EMC电磁兼容标准解析

前言 电磁兼容性&#xff08;ElectromagneTIc CompaTIbility&#xff0c;EMC&#xff09;是指设备或系统在其电磁环境中能正常工作且不对该环境中任何事物构成不能承受的电磁骚扰的能力&#xff3b;1-2&#xff3d;。电磁兼容性标准即是对设备或系统的这个能力提出的要求。对医…

GY-906

MLX90614 系列红外测温模块的原理及应用 南京航空航天大学 曾德志 摘要&#xff1a; MLX90614 系列模块是一组通用的红外测温模块。在出厂前该模块已进行校验及线 性化&#xff0c;具有非接触、体积小、精度高&#xff0c;成本低等优点。被测目标温度和环境温度能通过单通…

锤子yq601安装xposed框架

手机必须root&#xff0c;root后用奇兔刷机刷入奇兔的recovery这是用来刷入xposed框架的&#xff0c;官方recovery不支持 准备工作 1.下载一个对应的xposed框架&#xff0c;锤子yq601 5.1.1 可用框架传送&#xff0c;然后手机进入recovery刷入 2.下载一个xposed install apk&am…

锤子坚果手机(YQ601)换屏过程分享

欢迎转载&#xff0c;请注明出处&#xff1a;http://blog.csdn.net/yanlai20/article/details/50765441 最近太忙了&#xff0c;好长时间都没有整理&#xff0c;看有不少的用户访问&#xff0c;放空炮实在不好意思&#xff0c;先贴点过程图 大概说下难点&#xff1a; 1、拆电池…