资源描述
操作系统课程设计
一个用户级文件系统的设计
目录
1、课程设计的主要目的-------------------------------------------3
2、相关的技术背景-----------------------------------------------3
2.1. 文件系统简介--------------------------------------------3
2.2. FUSE简介-----------------------------------------------3
2.3. 解压FUSE-----------------------------------------------4
2.4. 编译并安装FUSE-----------------------------------------5
3、主要思想和技术路线-------------------------------------------5
3.1. 数据结构定义--------------------------------------------5
3.2. 主要函数定义--------------------------------------------5
3.2.1.u_fs函数-------------------------------------------6
3.2.2.base函数-------------------------------------------8
4、测试结果-----------------------------------------------------9
4.1. 创建文件系统-------------------------------------------9
4.2. 文件夹测试--------------------------------------------10
4.3. 文件测试----------------------------------------------11
4.4. 测试格式化功能----------------------------------------12
4.5. 卸载文件系统------------------------------------------12
5、源代码的目录结构及存放位置----------------------------------13
6、运行环境----------------------------------------------------13
1课程设计的主要目的
更加深入地理解文件系统的原理和设计,了解在linux平台下的编程,加强学生动手能力,丰富代码经验。
实现两层目录系统,有以下的要求和限制:
1.根目录可以包含子目录和普通文件
2.子目录只能包含普通文件,不能包括其他子目录
3.所有文件都能可读写(改变模式0666),忽略权限
4.许多文件属性如创建和修改时间不需要准确地储存,
5.文件不能被截断
6.目录看成是文件
2.相关的技术背景
2.1文件系统
文件系统是一种用来存储和组织计算机文件、目录及其包含的数据的方法,它使文件、目录以及数据的查找和访问得到简化。文件系统能提供丰富的扩展能力。它可以编写成底层文件系统的一个封装程序,从而对其中的数据进行管理,并提供一个增强的、具有丰富特性的文件系统。
2.2FUSE简介
FUSE是linux平台下使用用户空间的文件系统。我们不需要了解linux内核模块的编程知识,只要利用FUSE提供的文件系统框架,就可以创建自己的功能完备的文件系统。
FUSE主要是由以下的三部分构成:
l 内核模块FUSE
l 用户空间库libfuse
l mount/umount程序fusermount
在 FUSE 中创建一个文件系统,先安装一个 FUSE 内核模块,然后使用 FUSE 库和 API 。
用户空间进程在执行操作文件的系统调用的时候,在内核空间,VFS就会调用各文件系统定义的对应操作函数。FUSE内核模块中被定义的操作函数把和它对应的请求送到实现文件系统的用户空间进程(FUSE文件系统,也就是后台程序),并等待回应。FUSE内核模块和FUSE文件系统间的通信是通过设备文件/dev/fuse进行的。FUSE文件系统把定义的FUSE操作函数群的地址登录到fuse_operations结构体中,并通过把fuse_operations的地址作为参数,调fuse_main()函数
以下的图指出在example/hello的例子中,文件系统调用的路径。
1. 打开设备文件/dev/fuse
2. 挂载FUSE文件系统
3. 做成FUSE文件系统句柄
4. 登录FUSE操作函数到FUSE文件系统句柄中
5. 登录信号处理器
6. 执行事件循环
A) 从设备文件/dev/fuse中读取来自内核模块的请求
B) 执行和这个请求对应的操作函数
C) 写入返回给内核模块的应答到设备文件/dev/fuse中
7. 卸载FUSE文件系统
在库函数fuse_main()中,执行以下的动作。
API库里定义的接口如getattr, mknod,mkdir,unlink等包含在./include/fuse.h头文件里。
2.3解压 FUSE
要开发一个文件系统,首先请下载 FUSE 的源代码并展开这个包:tar -zxvf fuse-2.7.0.tar.gz。这会创建一个 FUSE 目录,其中保存的是源代码。fuse-2.7.0 目录的内容如下:
./doc 包含了与 FUSE 有关的文档。现在,这只有一个文件 how-fuse-works。
./kernel 包含了 FUSE 内核模块的源代码(对于使用 FUSE 开发文件系统来说,您当然不用懂得这些代码的机制)。
./include 包含了 FUSE API 头,您需要这些文件来创建文件系统。您现在唯一需要的就是 fuse.h。
./lib 中存放的是创建 FUSE 库的源代码,您需要将它们与您的二进制文件链接在一起来创建文件系统。
./util 中存放的是 FUSE 工具库的源代码。
./example 当然包含的是一些供您参考的例子,例如 fusexmp.null 和 hello 文件系统。
2.4编译并安装 FUSE
在 fuse-2.7.0 目录中运行 configure 脚本: ./configure。这会创建所需要的 makefile 等内容。
运行 ./make 来编译库、二进制文件和内核模块。查看 kernel 目录中的文件 ./kernel/fuse.ko —— 这是内核模块文件。还要查看 lib 目录中的 fuse.o、mount.o 和 helper.o。
运行 ./make install 完成 FUSE 的安装。
重要提示:在编译 FUSE 时,系统中需要有内核头文件或源代码。为了简单起见,请确保将内核源代码放到 /usr/src/ 目录中。
3主要思想和技术路线
3.1数据结构定义
u_fs文件系统使用一个镜像文件(本机测试为ufs_iso文件),空间分配如下。
Super block
(1 block)
Bitmap block
(1280 blocks)
Data block
(all the rest blocks)
测试时分配了1K * 5K = 5M空间,共10240块。
超级块 super block
超级块一定是文件系统的第一块,描述了整个文件系统,结构定义如下:
struct sb {
long fs_size; //整个文件系统的块数
long first_blk; //根目录所在的第一块
long bitmap; //位图块大小
}
目录 Directory
目录应看做文件。每个目录下包含一个u_fs_directory_entry结构的表。在本文件系统没有对目录个数作出限制,但要注意文件名不得超过8字节,拓展名不超过3字节。
struct u_fs_file_directory {
char fname[MAX_FILENAME + 1]; //文件名
char fext[MAX_EXTENSION + 1]; //拓展名
size_t fsize; //大小
long nStartBlock; //起始块
int flag; //类型:0-未使用 1-文件 2-目录
}
文件 Files
文件存储在单一、预分配的虚拟磁盘上,每个虚拟块大小为512字节。
struct u_fs_disk_block {
size_t size; //该块用了多少字节
long nNextBlock; //下一块的指针
char data[MAX_DATA_IN_BLOCK]; //可用的虚拟存储空间
};
3.2主要函数定义
3.2.1.u_fs函数定义
fuse_operation 结构中用到的函数
struct fuse_operations {
int (*getattr) (const char *, struct stat *);
int (*readdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
int (*mknod) (const char *, mode_t, dev_t);
int (*mkdir) (const char *, mode_t);
int (*unlink) (const char *);
int (*rmdir) (const char *);
int (*open) (const char *, struct fuse_file_info *);
int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *);
int (*write) (const char *, const char *, size_t, off_t,struct fuse_file_info *);
int (*flush) (const char *, struct fuse_file_info *);
int (*truncate) (const char *, off_t);
int (*init) (struct fuse_conn_info *);
};
int (*getattr) (const char *, struct stat *);
/*这个函数与 stat() 类似。st_dev 和 st_blksize 域都可以忽略。st_ino 域也会被忽略,除非在执行 mount 时指定了 use_ino 选项。*/
int (*readdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
/*这个函数会读取一个目录中的内容。这个操作实际上是在一次调用中执行 opendir()、readdir()、closedir() 序列。对于每个目录项来说,都应该调用 filldir() 函数。*/
int (*mknod) (const char *, mode_t, dev_t);
/*这个函数会创建一个文件节点。此处没有 create() 操作;mknod() 会在创建非目录、非符号链接的节点时调用。*/
int (*mkdir) (const char *, mode_t);
int (*rmdir) (const char *);
/*这两个函数分别用来创建和删除一个目录。 */
int (*unlink) (const char *);
/*这个函数用来删除一个文件。 */
int (*open) (const char *, struct fuse_file_info *);
/*这是文件的打开操作。对 open() 函数不能传递创建或截断标记(O_CREAT、O_EXCL、O_TRUNC)。这个函数应该检查是否允许执行给定的标记的操作。另外,open() 也可能在 fuse_file_info 结构中返回任意的文件句柄,这会传递给所有的文件操作。)*/
int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *);
/*这个函数从一个打开文件中读取数据。除非碰到 EOF 或出现错误,否则 read() 应该返回所请求的字节数的数据;否则,其余数据都会被替换成 0。一个例外是在执行 mount 命令时指定了 direct_io 选项,在这种情况中 read() 系统调用的返回值会影响这个操作的返回值。*/
int (*write) (const char *, const char *, size_t, off_t, struct fuse_file_info *);
/*这个函数将数据写入一个打开的文件中。除非碰到 EOF 或出现错误,否则 write() 应该返回所请求的字节数的数据。一个例外是在执行 mount 命令时指定了 direct_io 选项(这于 read() 操作的情况类似)。*/
int (*flush) (const char *, struct fuse_file_info *);
/*这表示要刷新缓存数据。它并不等于 fsync() 函数 —— 也不是请求同步脏数据。每次对一个文件描述符执行 close() 函数时,都会调用 flush();因此如果文件系统希望在 close() 中返回写错误,并且这个文件已经缓存了脏数据,那么此处就是回写数据并返回错误的好地方。由于很多应用程序都会忽略 close() 错误,因此这通常用处不大。*/
int (*truncate) (const char *, off_t);
/*这个函数用来修改文件的大小。*/
int (*init) (struct fuse_conn_info *);
/*这个函数用来对加载的文件系统初始化,如:获得文件系统的大小。*/
3.2.2.base函数定义
为u_fs的FUSE用户操作提供底层操作,在base.c定义供u_fs调用。
int op_read_blk(long blk,u_fs_disk_block * content);
//读取块中的内容
int op_write_blk(long blk,u_fs_disk_block * content);
//往块中写入内容
int op_search_free_blk(int num,long* start_blk);
//寻找空闲块,采用首次匹配法
int op_set_blk(long blk,int flag);
//设置块的flag位
int op_create(const char *, int flag);
op_create( )
//函数把创建目录和文件统一起来。
int op_open(const char * org_path, u_fs_file_directory *attr);
op_open( )
/*返回要打开的目录或文件的u_fs_file_directory记录,根据此记录可以知道目录或文件所在的数据块,大小等属性,以备调用的函数使用。*/
int op_setattr(const char* org_path, u_fs_file_directory * attr);
op_setattr( )
/*更改文件或目录的u_fs_file_directory记录。在删除文件或目录,使用此函数可使flag设为0。在写文件时,调用此函数可以更改文件的大小。*/
int op_rm(const char *path,int flag);
op_rm( )
/*函数把删除目录和文件统一起来。删除时,要把对应的数据块释放;把u_fs_file_directory记录的flag标记为0。*/
4测试结果
注意:
cy采用宏定义,即在u_fs.h里对路径进行定义。
现定义为#define UFS_ISO “/home/chenyue/aimao/ufs_iso”
此值应根据不同系统改成对应的绝对路径。
4.1创建文件系统
1. 分配磁盘空间
dd bs=1K count=5K if=/dev/zero of=./ufs_iso
2. 格式化磁盘空间
./u_fs_init
3. 创建装载位置
mkdir cy 装载ufs_iso文件系统到cy文件夹,发现在位置一栏新增了“cy”盘。./u_fs cy
4. 查看cy文件夹内容,发现为空
ls –al cy
4.2文件夹测试
5. 进入文件系统
cd cy
6. 创建超长文件夹,操作不允许
mkdir abcdefighijk
7. 创建普通文件夹
mkdir rootdir
8. 进入刚创建的dir文件夹
cd rootdir
9. 创建子文件夹,操作不允许
mkdir leftdir
4.3文件测试
10. 退回到根目录,查看不存在的文件,提示错误
cd ..
cat file
11. 创建并编辑文件,添加“Hello,world!!!”内容
gedit hello
12. 查看hello文件
cat hello
13. 测试获取文件列表的功能
ls –al
14. 删除不存在的文件 nofile
rm nofile
15. 删除存在的文件 hello,并查看
rm hello
ls –al
4.4格式化功能测试
16. 回到文件系统父目录,查看已有文件
ls –al cy
17. 格式化
./u_fs_init
18. 再次查看,发现已经清空
ls –al cy
4.5卸载文件系统
19. 卸载文件系统
fusermount –u cy
文件系统已经卸载
5源代码的目录结构及存放位置
源码包含:
u_fs.h
包含数据结构定义及各种常数
u_fs_inti.c
提供格式化操作
bottom.c
对bottom.h的底层函数进行定义
bottom.h
提供底层函数供u_fs.c调用
u_fs.c
文件系统的主函数
makefile
提供编译、卸载的功能
6运行环境
系统: Ubuntu9.04
编译器: gcc
附加库: fuse-2.7.0
13
展开阅读全文