提交截止时间为5月20日晚23:00,请注意GitHub将在截止时间到达时锁定你的仓库,不再接受变更。
请大家通过此链接在GitHub上提交实验三。
本实验旨在通过编写一个基于内存的文件系统,加深对文件系统和内存管理方面的理解。
注:本实验不强制要求使用何种编程语言、使用FUSE、或使用Linux操作系统,只要完成实验要求即可,但为了叙述方便,以下文档使用C语言在Linux上通过FUSE实现。
本文档默认使用64位Linux操作系统,使用gcc编译器,并使用目前大部分Linux操作系统自带的2.9.x版本的libfuse为例编写,请先安装相应的编译环境,并安装libfuse(如在Ubuntu下安装libfuse-dev软件包,或在Gentoo下安装sys-fs/fuse软件包)。
FUSE是用户空间文件系统,它避免了在内核模式开发文件系统,可以大幅简化工作量,但不可避免的会带来额外的内核态/用户态的开销。
请先创建一个oshfs.c
文件,内容如下:
#define FUSE_USE_VERSION 26
#include <fuse.h>
static const struct fuse_operations op;
int main(int argc, char *argv[])
{
return fuse_main(argc, argv, &op, NULL);
}
使用如下指令编译:
$ gcc -D_FILE_OFFSET_BITS=64 -o oshfs oshfs.c -lfuse
新建挂载点:
$ mkdir mountpoint
之后即可使用oshfs
挂载你的文件系统到mountpoint上:
$ ./oshfs mountpoint
此时可以通过mount
命令确认你的文件系统已被挂载,但如果运行ls mountpoint
,则会出现以下错误提示:
$ ls mountpoint
ls: cannot access 'mountpoint': Function not implemented
说明我们还未实现相关函数,因此此文件系统不支持列目录操作。
在进行以下实验之前,请大家先了解一下FUSE的工作原理。
libfuse自身有几个简单的例子,可参见libfuse的代码。
其中fusexmp是一个简单的passthrough,可供参考。
hello则是一个简单的只读文件系统,其中只有一个hello文件,存放了”Hello World!\n”字符串。
既然是内存文件系统,那么最简单的实现也就是直接将文件列表的链表和文件内容直接存放在malloc分配的内存中,一个简单的实现见示例程序。
此程序可创建、修改文件,但未实现删除文件,也不支持目录操作。
你需要自行实现对文件的删除操作,对于目录操作的实现则不做强制要求。
在上述示例程序中,我们采用了malloc分配内存,在本实验中需要自行使用mmap向操作系统申请一块内存,并自行决定存储空间的分配算法。
mmap和munmap的使用方式可参考示例代码的全局mem变量和init函数中的两个demo。
全局mem变量保存了每个block的内存指针(NULL即代表尚未分配),我们应该将所有的文件系统信息均存储于mem指向的内存中。换句话说,我们在此实验中,在用户态模拟实现一个换页算法,mem指针数组的每个指针均指向一个页面的进程实际地址,其总体构成一个线性地址空间,供我们在实现文件系统时使用。
init的Demo 1展示了如何通过mmap分配一个blocksize的空间;Demo 2中展示了你可以通过一个系统调用获得一块连续的空间,然后自行切分,可单独对任意内存区域进行munmap操作。
你可以使用基本的Linux命令测试你的文件系统,比如:
$ cd mountpoint
$ ls -al
$ echo helloworld > testfile
$ ls -l testfile # 查看是否成功创建文件
$ cat testfile # 测试写入和读取是否相同
$ dd if=/dev/zero of=testfile bs=1M count=2000
$ ls -l testfile # 测试2000MiB大文件写入
$ dd if=/dev/urandom of=testfile bs=1M count=1 seek=10
$ ls -l testfile # 此时应为11MiB
$ dd if=testfile of=/dev/null # 测试文件读取
$ rm testfile
$ ls -al # testfile是否成功删除
本实验不要求文件系统的性能,但这里提出几个问题,可以供大家思考:
首先是过多的链表操作。比如如果对于文件的随机读写请求都需要遍历以便读写位置前的链表所有节点,是否存在效率问题。
其次是对齐。如果将每个block作为链表的一个结点,那么一个block实际存储的信息便不是一个整数,比如对于4KiB的block文件系统,那么每个block会存储3.9KiB的文件内容和一个next指针,这对一些期望文件物理对齐的应用不友好,比如一个存储raw格式的虚拟磁盘,在其上还有一个文件系统的情况。
此外关于兼容性。关于大小端序的问题,由于内存文件系统一般在同一个cpu上操作,因此可以不考虑。但关于文件元信息,直接将struct stat存储在文件系统中是否是一个好的做法?
文件系统存在一些合理的使用限制可以方便我们开发,比如ext4限制文件名255字符、最大文件大小16TiB、最大文件数目等等。但限制最大文件4KiB、最多10个文件显然不是合理的限制。注意本实验主要考察存储管理算法,设定不合理的限制一般意味着你的算法本身不合理。对于今天来说,GiB量级的文件操作是很常见的。
可以通过-f参数使程序在前台运行,可以直接通过stdio输出调试信息:
$ ./oshfs -f mountpoint
或者通过-o debug选项使程序在前台运行,并使libfuse输出调试信息:
$ ./oshfs -o debug mountpoint
可参考fusexmp了解各函数对应的Linux文件接口调用,并相应编写程序,测试是否正确能正确执行对应的函数,并将在你的文件系统上执行的结果与其他操作系统内置的文件系统(tmpfs、ext4等)的结果对比是否相同。
本次实验将采用人工批改与计算机自动批改相结合的方式,凡确认的抄袭行为将导致本次实验0分,请不要复制他人的代码。