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).

linux服务器filesystem,Linux Filesystem in Userspace(FUSE)相关推荐

  1. 什么是linux服务器?linux服务器优点有哪些?

    知识拓展1:什么是linux服务器? Linux服务器就是采用Linux系统的网络服务器,同时也有采用windows的服务器,作用是类似的.而Linux并不是一个特定的系统,而是使用Linux内核的系 ...

  2. linux服务器lvs,Linux的企业-LVS(Linux虚拟服务器)及FULLNAT设置

    一.Linux Virtual Server (linux虚拟服务) lvs工作于IOS七层模型的传输层,通过对TCP.UDP.SCTP.IPsec ESP.AH这些工作在四层的协议的支持,根据目标地 ...

  3. linux服务器视窗,linux窗口管理器介绍

    很多LINUX初学者分不清楚linux和X之间,X和Xfree86之间,X和KDE,GNOME等之间是什么关系.常常混淆概念,我想以比较易 于理解的方式说明一下X,X11,XFREE,WM,KDE,G ...

  4. linux服务器防病毒,Linux系统中你不需要防病毒?_服务器评论-中关村在线

    误区4:Linux是无病毒. Linux的安全性这么好,这是否意味着Linux是无病毒吗? 现实:Linux是非常安全,并不是没有针对Linux方面的病毒.有许多针对Linux的已知病毒.但是几乎所有 ...

  5. linux服务器拓扑图,Linux服务器作为网关的DNS分离解析服务(CentOS 7版本)

    一.DNS分离解析服务拓扑图 由Linux服务器作为网关,通过NAT地址转换的功能,根据不同网络的来访者,解析相同的域名从而得到不一样的IP地址.即网络被分为私网(局域网)和公网(广域网)两个部分,N ...

  6. Arch Linux 服务器,Arch Linux不适合当作服务器操作系统的四大原因

    摘要 腾兴网为您分享:Arch Linux不适合当作服务器操作系统的四大原因,云闪付,一路捞,武魂传说,完美志愿等软件知识,以及重复文件清理软件,涂色本,安卓福利社,cc网易cc直播,德化教育管理公共 ...

  7. 如何远程linux服务器桌面,LINUX操作系统如何远程登录桌面

    相比window来说linux远程桌面登陆需要第三方软件来实现,之前用ssh登陆的都是字符界面;昨天试了试远程桌面也是可以的,方法很多,这儿只阐述比较好用的一种;使用时,关闭windows防火墙,或者 ...

  8. linux服务器下降,Linux服务器CPU占用率上升速度下降的解决

    在linux服务器运行中,有时候会出现下列现象: CPU占用率很高,超过10,并且各种程序都轻松占用到90%,web访问很慢,基本无法执行,邮件投递基本无响应. 遇到上述情况,可执行dmesg查看是否 ...

  9. linux服务器防病毒,Linux服务器防病毒实战(3)

    随着Linux应用的日益广泛,有大量的网络服务器使用Linux操作系统.由于Linux的桌面应用和Windows相比还有一定的差距,所以在企业应 用中往往是Linux和Windows操作系统共存形成异 ...

  10. 参观linux服务器机房,LINUX服务器在双线机房实现方法

    LINUX服务器在双线机房实现方法 序言: 多线路接入技术就是在互联网数据中心(IDC)通过特殊的技术手段把不同的网络接入商(ISP)服务接入到一台服务器上或服务器集群,使服务器所提供的网络服务访问用 ...

最新文章

  1. SQL操作的组成部分-数据查询
  2. linux内核环境变量,Linux上安装配置JDK环境变量
  3. 通过Ajax方式上传文件(input file),使用FormData进行Ajax请求
  4. python处理网页弹窗_Selenium2+Python自动化-处理浏览器弹窗(转载)
  5. 2500万美元和AI专家!谷歌出钱出人,要用AI做“对社会有益”的事情
  6. Mysql基础系列(一)
  7. Keras 开发你的第一个 Python 深度学习项目
  8. php json_encode方法 报错:Inf and NaN cannot be JSON encoded
  9. 安卓kali安装mysql_超详细安卓手机安装kali教程(root篇)
  10. 昆仑万维上半年营收22.5亿同比降2%:净利6.4亿 同比降35.6%
  11. eleme饿了么vue项目随笔,随时更新,想到哪里写到哪里比较凌乱一直更新
  12. 若依对接企业微信JS-DK
  13. 神通数据库connect by用法
  14. 排列组合 离散数学_排列组| 离散数学
  15. 走进Java Android 的线程世界(三)Hander消息机制
  16. 【mybatis】mybatis的工作原理
  17. 关于BMP格式图片在终端显示的具体做法(超详细)
  18. 一台电脑同时配置github私人账户和公司账户
  19. 苹果公司对失败的总结和展望未来
  20. java无法检测到_使用JVM管理工具jvisualvm,系统提示无法检测到本地java应用程序的原因和解决办法...

热门文章

  1. 利用python爬取王者荣耀皮肤壁纸
  2. 设计模式学习总结系列应用实例
  3. 北京玉符飞扬科技面经(一面拿到offer)面试官是facebook的大牛
  4. 分享邮件营销群发的6大技巧!怎么群发邮件效果好?
  5. Dell戴尔笔记本电脑Vostro 14 5410原装出厂WIN10系统恢复原厂OEM系统
  6. ora**cle数据库过期问题
  7. JSP 返回上一页的几种方法
  8. 抢票显示服务器失败是什么原因,抢票网站的手机核验失败原因
  9. Altium Designer18之修改画图光标大小
  10. ❤️数据可视化❤️:基于Echarts + GeoJson实现的地图视觉映射散点(气泡)组件【10】 - 黑龙江省