现在的位置: 首页 > 综合 > 正文

Linux Filesystem in Userspace(FUSE)

2014年10月10日 ⁄ 综合 ⁄ 共 12454字 ⁄ 字号 评论关闭

转贴两篇关于fuse的文章,后面再研究

 Linux Filesystem in Userspace(FUSE)

http://www.360doc.com/content/09/1008/23/175938_6995356.shtml

1. FUSE(Filesystem in Userspace)简介
1.1. 概述
FUSE(用户空间文件系统)作为类UNIX系统平台上可加载的内核模块,允许非特权用户创建功能完备的文件系统,而不需要重新编译内核。FUSE模块仅仅提供kernel模块的接入口,而本身的主要实现代码位于用户空间中。对于读写虚拟文件系统来说,FUSE是个很好的选择。
FUSE起初是为了研究AVFS(A Virtual Filesystem)而设计的,而现在已经成为SourceForge的一个独立项目,目前适用的平台有Linux, FreeBSD, NetBSD, OpenSolaris和Mac OS X。官方的linux kernel版本到2.6.14才添加了FUSE模块,因此2.4的内核模块下,用户如果要在FUSE中创建一个文件系统,需要先安装一个FUSE内核模块,然后使用FUSE库和API来创建。
1.2. FUSE具有的特点如下:
? 库文件简单
? 安装简便,不需要加补丁或者重新编译kernel
? 执行安全,使用稳定
? 实现用户空间——kernel接口高效
? 非特权用户可以使用
? 基于linux2.4.x和2.6.x内核,现在可以支持JavaTM 绑定,不必限定使用C和C++来编写文件系统
1.3. 支持的特殊文件系统
NTFS-3G, GlusterFS, SSHFS, GmailFS, EncFS…
1.4. FUSE商业实现与学术实现
? LUFS是一个混合用户空间的文件系统框架,对用于任何应用程序无数的文件系统提供透明支持。大部分LUFS系统包括一个内核模块和一个用户空间守护进程,将大部分VFS调用都委托个一个专用的守护进程处理
? UserFS让用户文件系统可以像普通文件系统一样加载
? Ufo Project是为Solaris提供的一个全局文件系统,允许用户将远程文件真正当作本地文件一样对待
? OpenAFS是Andrew FileSystem的一个开源版本
? CIFS是Common Internet FileSystem的简称
1.5. FUSE展开
? ./doc包含FUSE相关文档
? ./kernel包含了FUSE内核模块的源代码
? ./include包含了FUSE API头,对创建文件系统有用,主要用fuse.h
? ./lib中存放FUSE库的源代码
? ./util包含了FUSE工具库的源代码
? ./example参考的例子
2. FUSE的安装
可以在FUSE主页上下载稳定的开源源代码(http://fuse.sourceforge.net/),由于编译内核兼容的原因,我下载的是fuse-2.4.2。
2.1. 安装办法1
在linux服务器上安装:
1. 解压包:tar –zxvf fuse-2.4.2
2. 在fuse目录中运行configure脚本:. /configure,这会创建所需要的makefile等文件。configure后还可以加一些选项参数,通过./configure –h(help)可以看到相关设置。如果需要编译内核,请加上./configure –enable-kernel-module,如果你的内核版本低于2.6.14的话,这项操作就是必须的啦。
3. 运行./make来编译库、二进制文件和内核模块。如果编译了FUSE kernel模块,可以看到./kernel/fuse.ko(fuse.o)。lib目录下有fuse.o、mount.o和helper.o。
4. 运行./make install完成FUSE的安装
2.2. 安装办法2
在我最近从事的项目中,涉及到ntfs-3g,而ntfs-3g.soureforge上说明不需要安装FUSE相关库文件及组件,于是将kernel部分移植进了avs730内核模块。
1. 将FUSE内核源代码拷贝到linux-2.4.x\fuse中
2. 拷贝FUSE相关头文件到fuse目录
3. 编写makefile:

4. 修改系统内核文件系统下的makefile,添加fuse.o模块编译链接
5. 重新编译内核
3. FUSE流程
3.1. FUSE Userspace
3.1.1. 具体流程
fuse_main() (lib/helper.c)——fuse用户空间主函数,用户程序调用它时,fuse_main()函数解析相关参数(如mountpoint,multithreaded),并调用fuse_mount()函数。调用fuse_new()函数,为fuse文件系统数据分配存储空间。调用fuse_loop()函数实现会话的接受与处理。
fuse_mount() (lib/mount.c)——创建UNIX本地套接口,创建并运行子进程fusermount。并返回fuse模块文件fd给fuse_main()函数。
fusermount (util/fusermount.c)——确保fuse模块已经加载,通过UNIX套接口返回fuse模块的文件fd给fuse_mount()函数。
fuse_new() (lib/fuse.c)——为fuse创建数据结构空间,用来存储文件系统数据。
fuse_loop() (lib/fuse.c)( fuse_loop_mt() (lib/fuse_mt.c))——从/dev/fuse 读取文件系统调用,调用fuse_operations结构中的处理函数,返回调用结果给/dev/fuse
具体流程图如图表 1 fuse 用户空间流程图:

3.1.2. fuse_operation结构
struct fuse_operations {
int (*getattr) (const char *, struct stat *);
int (*readlink) (const char *, char *, size_t);
int (*mknod) (const char *, mode_t, dev_t);
int (*mkdir) (const char *, mode_t);
int (*unlink) (const char *);
int (*rmdir) (const char *);
int (*symlink) (const char *, const char *);
int (*rename) (const char *, const char *);
int (*link) (const char *, const char *);
int (*chmod) (const char *, mode_t);
int (*chown) (const char *, uid_t, gid_t);
int (*truncate) (const char *, off_t);
int (*utime) (const char *, struct utimbuf *);
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 (*statfs) (const char *, struct statvfs *);
int (*flush) (const char *, struct fuse_file_info *);
int (*release) (const char *, struct fuse_file_info *);
int (*fsync) (const char *, int, struct fuse_file_info *);
int (*setxattr) (const char *, const char *, const char *, size_t, int);
int (*getxattr) (const char *, const char *, char *, size_t);
int (*listxattr) (const char *, char *, size_t);
int (*removexattr) (const char *, const char *);
int (*opendir) (const char *, struct fuse_file_info *);
int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *);
int (*releasedir) (const char *, struct fuse_file_info *);
int (*fsyncdir) (const char *, int, struct fuse_file_info *);
void *(*init) (struct fuse_conn_info *conn);
void (*destroy) (void *);
int (*access) (const char *, int);
int (*create) (const char *, mode_t, struct fuse_file_info *);
int (*ftruncate) (const char *, off_t, struct fuse_file_info *);
int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *);
int (*lock) (const char *, struct fuse_file_info *, int cmd, struct flock *);
int (*utimens) (const char *, const struct timespec tv[2]);
int (*bmap) (const char *, size_t blocksize, uint64_t *idx);
};
这些操作并非都是必需的,但是一个文件系统要正常工作,就需要其中很多函数。读者可以实现一个具有特殊目的的.flush、.release或者.fsync方法的功能完备的文件系统。
下面是fuse_operation的具体介绍:
? getattr: int (*getattr) (const char *, struct stat *);
这个函数与 stat() 类似。st_dev 和 st_blksize 域都可以忽略。st_ino 域也会被忽略,除非在执行 mount 时指定了 use_ino 选项。

? readlink: int (*readlink) (const char *, char *, size_t);
这个函数会读取一个符号链接的目标。缓冲区应该是一个以 null 结束的字符串。缓冲区的大小参数包括这个 null 结束字符的空间。如果链接名太长,不能保存到缓冲区中,就应该被截断。成功时的返回值应该是 “0”。

? getdir: int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
这个函数会读取一个目录中的内容。这个操作实际上是在一次调用中执行 opendir()、readdir()、...、closedir() 序列。对于每个目录项来说,都应该调用 filldir() 函数。

? mknod: int (*mknod) (const char *, mode_t, dev_t);
这个函数会创建一个文件节点。此处没有 create() 操作;mknod() 会在创建非目录、非符号链接的节点时调用。
? mkdir: int (*mkdir) (const char *, mode_t);
? rmdir: int (*rmdir) (const char *);
这两个函数分别用来创建和删除一个目录。
? unlink: int (*unlink) (const char *);
? rename: int (*rename) (const char *, const char *);
这两个函数分别用来删除和重命名一个文件。
? symlink: int (*symlink) (const char *, const char *);
这个函数用来创建一个符号链接。
? link: int (*link) (const char *, const char *);
这个函数创建一个到文件的硬链接。
? chmod: int (*chmod) (const char *, mode_t);
? chown: int (*chown) (const char *, uid_t, gid_t);
? truncate: int (*truncate) (const char *, off_t);
? utime: int (*utime) (const char *, struct utimbuf *);
这 4 个函数分别用来修改文件的权限位、属主和用户、大小以及文件的访问/修改时间。
? open: int (*open) (const char *, struct fuse_file_info *);
这是文件的打开操作。对 open() 函数不能传递创建或截断标记(O_CREAT、O_EXCL、O_TRUNC)。这个函数应该检查是否允许执行给定的标记的操作。另外,open() 也可能在 fuse_file_info 结构中返回任意的文件句柄,这会传递给所有的文件操作。

? read: int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *);
这个函数从一个打开文件中读取数据。除非碰到 EOF 或出现错误,否则 read() 应该返回所请求的字节数的数据;否则,其余数据都会被替换成 0。一个例外是在执行 mount 命令时指定了 direct_io 选项,在这种情况中 read() 系统调用的返回值会影响这个操作的返回值。

? write: int (*write) (const char *, const char *, size_t, off_t, struct fuse_file_info *);
这个函数将数据写入一个打开的文件中。除非碰到 EOF 或出现错误,否则 write() 应该返回所请求的字节数的数据。一个例外是在执行 mount 命令时指定了 direct_io 选项(这于 read() 操作的情况类似)。

? statfs: int (*statfs) (const char *, struct statfs *);
这个函数获取文件系统的统计信息。f_type 和 f_fsid 域都会被忽略。
? flush: int (*flush) (const char *, struct fuse_file_info *);
这表示要刷新缓存数据。它并不等于 fsync() 函数 —— 也不是请求同步脏数据。每次对一个文件描述符执行 close() 函数时,都会调用 flush();因此如果文件系统希望在 close() 中返回写错误,并且这个文件已经缓存了脏数据,那么此处就是回写数据并返回错误的好地方。由于很多应用程序都会忽略 close() 错误,因此这通常用处不大。

注意:我们也可以对一个 open() 多次调用 flush() 方法。如果由于调用了 dup()、dup2() 或 fork() 而产生多个文件描述符指向一个打开文件的情况,就可能会需要这种用法。我们无法确定哪个 flush 操作是最后一次操作,因此每个 flush 都应该同等地对待。多个写刷新序列相当罕见,因此这并不是什么问题。
? release: int (*release) (const char *, struct fuse_file_info *);
这个函数释放一个打开文件。release() 是在对一个打开文件没有其他引用时调用的 —— 此时所有的文件描述符都会被关闭,所有的内存映射都会被取消。对于每个 open() 调用来说,都必须有一个使用完全相同标记和文件描述符的 release() 调用。对一个文件打开多次是可能的,在这种情况中只会考虑最后一次 release,然后就不能再对这个文件执行更多的读/写操作了。release 的返回值会被忽略。

? fsync: int (*fsync) (const char *, int, struct fuse_file_info *);
这个函数用来同步文件内容。如果 datasync 参数为非 0,那么就只会刷新用户数据,而不会刷新元数据。
? setxattr: int (*setxattr) (const char *, const char *, const char *, size_t, int);
? getxattr: int (*getxattr) (const char *, const char *, char *, size_t);
? listxattr: int (*listxattr) (const char *, char *, size_t);
? removexattr: int (*removexattr) (const char *, const char *);
这些函数分别用来设置、获取、列出和删除扩展属性。

3.2. FUSE Kernel 模块
FUSE Kernel模块由两部分组成:
proc文件系统组件:Kernel/dev.c——回应io请求到/dev/fuse。fuse_dev_read()函数负责读出文件,并将来自“list of request”结构体的命令返回到调用函数。fuse_dev_write ()负责文件写入,并将写入的数据置放到“req→out”数据结构中。
文件系统调用部分:kernel/file.c,kernel/inode.c,kernel/dir.c——调用request_send(),将请求加入到“list of request”结构体中,等待回复(reply)。
下表将举例说明在FUSE中文件系统的操作过程:
表格 1 unlink 在FUSE中的操作过程

3.2.1. fc的结构
struct fuse_conn {
…………………………
/** Readers of the connection are waiting on this */
wait_queue_head_t waitq;
/** The list of pending requests */
struct list_head pending;
/** The list of requests being processed */
struct list_head processing;
…………………………
};

4. NTFS文件系统实现
4.1. LINUX文件系统结构
文件系统是一个存储设备上的数据和元数据进行组织的机制。Linux文件系统接口实现为分层的体系结构,从而将用户接口层、文件系统实现和操作存储设备的驱动程序分隔开。图表2所示体系结构显示了用户空间和内核中与文件系统相关的组要组件之间的关系:

图表 2 Linux文件系统组件的体系结构
用户空间包含一些应用程序和GNU C库,它们为系统调用(打开、读取、写和关闭)提供用户接口。系统调用接口的作用就像是交换器,它将系统调用从用户空间发送到内核空间中的适当端点。VFS是底层文件系统的主要接口。这个组件导出一组接口,然后将他们抽象到各个文件系统,各个文件系统的行为可能差异很大。inode和dentry是两个针对文件系统对象的缓存,它们缓存最近使用过的文件系统对象。
每个文件系统实现导出一组通用接口,供VFS使用。缓冲区缓存会缓存文件系统和相关块设备之间的请求。
4.2. NTFS文件系统操作的实现

图表 3 NTFS-FUSE文件系统结构
在ntfs-3g的源代码中已经内嵌了fuse library的相关组件,集中为libfuse。其中提供了Fuse Userspace的功能。于是ntfs-3g的libfuse部分就在挂载的时候建立fuse相关结构体,提供内存空间存储文件系统数据,挂载后,返回句柄fh给fuse_loop()。当有某个客户端的请求时,FUSE 内核模块将请求传送到/dev/fuse文件中,此时fuse_loop()读取到与fh相应的请求,立即调用fuse_operation进行处理,然后将处理结果(response)返回到客户端,这样就完成了单个请求的通信过程。
另外,通过图表3可以知道,Fuse内核模块与ntfs-3g的通信的接口,准确的说是Fuse内核模块与内嵌于ntfs-3g的libfuse的通信接口是一个特殊的文件/dev/fuse,内核模块将客户端的请求送到/dev/fuse并接受处理信息,返回到客户端;而用户空间的libfuse则读取与/dev/fuse文件句柄,接受系统调用信息后,调用fuse_loop()进行相应的处理,再将处理结果返回给/dev/fuse。
5. 小结
本文主要介绍了Filesystem in Userspace(FUSE)的特点、结构,以及在LINUX平台上安装的方法,另外将FUSE分为两大模块——Userspace模块和Kernel模块,进行讲解,并选用Linux ntfs文件系统实现的实例分析了FUSE的流程。

图表 1 fuse 用户空间流程图

图表 2 Linux文件系统组件的结构

图表 3 NTFS-FUSE文件系统的结构


unlink操作在FUSE(1)

unlink操作在FUSE(2).

 

fuse-用户空间文件系统(Filesystem in Usese Space)

文章出处:http://www.diybl.com/course/6_system/linux/linuxjq/20110102/552196.html

用户空间文件系统(Filesystem in Usese Space,简称FUSE)是操作系统中的概念,指完全在用户态实现的文件系统,FUSE提供了在用户空间的程序中实现文件系统的接口。目前Linux通过内核模块对此进行支持。一些文件系统如ZFS,glusterfs和luster使用FUSE实现。

Linux用于支持用户空间文件系统的内核模块名叫FUSE,FUSE一词有时特指Linux下的用户空间文件系统。

文件系统是一个通用操作系统重要的组成部分。传统上操作系统在内核层面上对文件系统提供支持;而通常内核态的代码难以调试,生产率较低。

FUSE结构
FUSE主要是由以下的三部分构成。
* 内核模块fuse
* 用户空间库libfuse
* mount/umount程序fusermount

用户空间进程在实行操作文件的系统调用的时候,在内核空间,VFS就会调用各文件系统定义的对应操作函数。FUSE内核模块中被定义的操作函数是把,和它对应的请求送到实现文件系统的用户空间进程(FUSE文件系统.后台程序),并等待回应。FUSE内核模块和FUSE文件系统.后台程序间的通信是通过设备文件/dev/fuse进行的。FUSE文件系统.后台程序把定义的FUSE操作函数群的地址登录到fuse_operations结构体中,并通过把 fuse_operations的地址作为参数,调用fuse_main()函数。

在库函数fuse_main()中,实行以下的动作。
1. 打开设备文件/dev/fuse

2. 挂载FUSE文件系统

3. 做成FUSE文件系统句柄

4. 登录FUSE操作函数到FUSE文件系统句柄中

5. 登录信号处理器

6. 实行事件循环
A) 从设备文件/dev/fuse中读取来自内核模块的请求
B) 实行和这个请求对应的操作函数
C) 写入返回给内核模块的应答到设备文件/dev/fuse中

7. 卸载FUSE文件系统

现在,API库里已经定义了31个接口 (getattr,readlink,getdir,mknod,mkdir,unlink,rmdir,symlink,rename,link,chmod,chown,truncate,utime,open,read,write,statfs,flush,release,fsync,setxattr,getxattr,listxattr,removexattr,opendir,readdir,releasedir,fsyncdir,init,destroy),它们可以被任意使用,

FUSE文件系统.后台程序实际上是否真的实现是自由的。并且,它们的大部分是把文件的路径作为第一个参数。

Linux从2.6.14版本开始通过FUSE模块支持在用户空间实现文件系统。在用户空间实现文件系统能够大幅提高生产率,简化了为操作系统提供新的文件系统的工作量,特别适用于各种虚拟文件系统和网络文件系统,ZFS和glusterfs都属于网络文件系统。但在用户态实现文件系统必然会引入额外的内核态/用户态切换带来的开销,对性能会产生一定影响。

 

500)this.width=500;" border="0">

 

Linux中,FUSE的运行机制

目前Linux,FreeBSD,NetBSD,OpenSolaris和Mac OSX支持用户空间态文件系统,比较知名的用户空间文件系统
* ExpanDrive: 商业文件系统,实现了SFTP/FTP/FTPS协议;
* GlusterFS: 用于集群的分布式文件系统,可以扩展到PB级;
* SSHFS: 通过SSH协议访问远程文件系统;
* GmailFS: 通过文件系统方式访问GMail;
* EncFS: 加密的虚拟文件系统
* NTFS-3G和Captive NTFS, 在非Windows中对NTFS文件系统提供支持;
* WikipediaFS : 支持通过文件系统接口访问Wikipedia上的文章;
* Sun公司的Lustre: 和GlusterFS类似但更早的一个集群文件系统
* ZFS: Luster的Linux版;
* archivemount:
* HDFS: Hadoop提供的分布式文件系统。HDFS可以通过一系列命令访问,并不一定经过Linux FUSE;

With FUSE it is possible to implement a fully functional filesystem in a userspace program.  Features include:
* Simple library API
* Simple installation (no need to patch or recompile the kernel)
* Secure implementation
* Userspace - kernel interface is very efficient
* Usable by non privileged users
* Runs on Linux kernels 2.4.X and 2.6.X
* Has proven very stable over time

How does it work?
The following figure shows the path of a filesystem call  (e.g. stat) in the above hello world example: The FUSE kernel module and the FUSE library communicate via a special file descriptor which is obtained by opening /dev/fuse.  This file can be opened multiple
times, and the obtained file descriptor is passed to the mount syscall, to match up the descriptor with the mounted filesystem.

500)this.width=500;" border="0">

在centos下对其的安装分体系架构而不同,先看x64的安装过程:
[root@smail ~]# rpm -aq|grep kernel
kernel-module-openafs-2.6.18-92.1.6.el5-1.4.7-68.2.SL5.i686
kernel-headers-2.6.18-92.1.6.el5.i386
kernel-module-fuse-2.6.18-92.1.6.el5-2.6.3-1.sl5.i686
kernel-devel-2.6.18-92.1.6.el5.i686
kernel-2.6.18-92.1.6.el5.i686

查看所安装的kernel包中,有没有安装kernel-devel这个包,没有则安装之;其次还要注意的是其版本一定要和主kernel版本完全一致(2.6.18-92.1.6.el5)。否则即使安装成功了fuse相关包后亦不能正常加载fuse模块。

如果通过yum查找不到,则可到rpm 相关下载站上进行搜索下载(http://rpm.pbone.net/)。

在x64的机器上可能通过安装如下的软件包来实现fuse的加载:
#yum install fuse fuse-libs.x86_64 fuse-devel.x86_64 dkms-fuse.noarch

而在x86的机器上操作如下:
#yum install fuse.i386 fuse-devel.i386 fuse-libs.i386
当然debian系列的操作系统就更好安装了,安装其基本包及开发包就可以了。

FAQ
---------------------
>关于这两个模块(FUSE、LUFS)的不同和为什么又开始了新的工程,说明一下好吗?

从SourceForge 上发布的日期来看,FUSE的最初发布比LUFS还要早一年。但在相当长的时间内,它们之前都没有相互注意。

在 LUFS里,文件系统是根据lufsmount被载入的共享对象(.so),而在FUSE里,文件系统是使用fuse库的各自实行可能的对象,这就是他们之间所谓的区别。实际的API都非常相似,都是载入LUFS模块/使用FUSE内核模块,然后能实行API(参照FUSE页的lufis)。

并且,LUFS在缓存目录和文件属性这一点上有一些不同。FUSE不是那样做的,但它提供了简单的接口。

---------------------
fuse_operations结构体里没有close()函数?

〉 close()函数没有包含在fuse_operations结构体里有理由吗?大概得有必要知道什么时候文件被关闭吧。

这并不简单。请考虑一下mmap()函数。把文件映射到内存里后,即使关闭了文件,那通过内存地址也能对这个文件进行读写操作。

然而,close()相似的函数还有flush和release函数。flush是 调用close(),release是包含内存映射的文件不再使用的时候在被调用的。

---------------------
FUSE为什么不支持ioctl。

因为对于规则文件实行ioctl没有什么意义,对于设备文件,文件系统的实现基本上不关心ioctl,所以怎么样使用它还不是很清楚。例如,为了支持设备 ioctl即使做了一些改良,因为它们包含任意的结构化的数据(不是读/写场合的那些长度),对于它们好像什么也不能做。

使用getxattr()和setxattr()的话,跟ioctl()比起来也是没有问题,这些都在fuse-2.0里已经实现了。

 

 

 

抱歉!评论已关闭.