![029794235febb3b0b6a01b043fd878c3.png](https://i-blog.csdnimg.cn/blog_migrate/ecd0d955ef094ae27a9bfbe679ad2275.png)
了解Linux文件系统的同学都清楚,Linux的文件系统都是在内核态开发的。内核态开发最大的问题是涉及的内容比价多,调试不太方便。因此,很多应用都不太原因涉及内核态的开发。而fuse解决了文件系统必须在内核态的的难题。 它将文件系统的实现从内核态搬到了用户态,也就是我们可以在用户态实现一个文件系统。fuse有很多应用,比如与对象存储(例如S3等)结合,可以实现通过普通目录访问云对象存储的方式,简化应用访问对象存储的复杂性。本号将陆续推出3-5篇文章介绍fuse,今天是第一篇,将介绍fuse的基本原理,及给出一个非常简单的示例程序。多说无益,我们接下来实际操作一下fuse。
软件安装
要使用该库进行用户态文件系统的开发,必需先安装。包括软件包和开发用的头文件等内容。不同的平台安装方法略有不同,下面分别介绍一下。Ubuntu 16.04
sudo apt-get install libfuse2 libfuse-dev
CentOS 6
yum install fuse fuse-devel
本文安装的是fuse-devel-2.9.4-1.0.4.el6.x86_64版本的,后续示例程序也是该版本的,如果版本不一致在测试时可能略有不同。
fuse基本原理
简单的理解,fuse实现了一个对文件系统访问的回调。fuse分为内核态的模块和用户态的库两部分。其中用户态的库为程序开发提供接口,也是我们实际开发时用的接口,我们通过这些接口将请求处理功能注册到fuse中。内核态模块是具体的数据流程的功能实现,它截获文件的访问请求,然后调用用户态注册的函数进行处理。
![ec97af3557ef9db02835571d573b32fa.png](https://i-blog.csdnimg.cn/blog_migrate/7520be204022f0093acd20286cf59af8.jpeg)
如图1所示,其中fuse_user是开发的用户态的文件系统程序,该程序启动的时候会将自己开发的接口注册到fuse中,比如读写文件的接口,遍历目录的接口等等。
同时,通过该程序在系统某个路径挂载fuse文件系统,比如/tmp/file_on_fuse_fs。此时如果在该目录中有相关操作时,请求会经过VFS到fuse的内核模块(上图中的步骤1),fuse内核模块根据请求类型,调用用户态应用注册的函数(上图中步骤2),然后将处理结果通过VFS返回给系统调用(步骤3)。
示例及说明
为了更好的理解如何使用fuse,我们这里实现一个具体的实例。该实例非常简单,这里并不是一个真正的文件系统,也不会访问磁盘,而是在该文件系统的根目录中显示一个固定的文件,也就是Hello-world文件。程序源代码
#define FUSE_USE_VERSION 26
#include <stdio.h>
#include <fuse.h>
/* 这里实现了一个遍历目录的功能,当用户在目录执行ls时,会回调到该函数,我们这里只是返回一个固定的文件Hello-world。 */
static int test_readdir(const char* path, void* buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info* fi)
{
printf( "tfs_readdir path : %s ", path);
return filler(buf, "Hello-world", NULL, 0);
}
/* 显示文件属性 */
static int test_getattr(const char* path, struct stat *stbuf)
{
printf("tfs_getattr path : %s ", path);
if(strcmp(path, "/") == 0)
stbuf->st_mode = 0755 | S_IFDIR;
else
stbuf->st_mode = 0644 | S_IFREG;
return 0;
}
/*这里是回调函数集合,这里实现的很简单*/
static struct fuse_operations tfs_ops = {
.readdir = test_readdir,
.getattr = test_getattr,
};
int main(int argc, char *argv[])
{
int ret = 0;
ret = fuse_main(argc, argv, &tfs_ops, NULL);
return ret;
}
编译运行
编译也是非常的简单,将上述内容复制到一个名为main.c文件中,然后执行如下命令:
gcc -o fuse_user main.c -D_FILE_OFFSET_BITS=64 -lfuse
假设在/tmp目录下面有一个file_on_fuse_fs目录,如果没有可以手动创建一个。然后执行如下命令可以在该目录挂载新的文件系统。
./fuse_user /tmp/file_on_fuse_fs
此时实际上已经有一个新的文件系统挂载在/tmp/file_on_fuse_fs目录下面,可以通过mount命令查看一下。
mount
可以看到输出内容中包含如下内容:
fuse_user on /tmp/file_on_fuse_fs type fuse.fuse_user (rw,nosuid,nodev)
也可以通过ls命令查看一下这个目录,比如:
ls -alh /tmp/file_on_fuse_fs
此时输出如下:
![28db905d61a3d8728b8c39686cfd45fd.png](https://i-blog.csdnimg.cn/blog_migrate/604c22a42e7ce541762aff787a0e6922.jpeg)
可以看出正式我们期望的内容,也就是只有一个名为Hello-world的文件。
至此,我们基本上了解了fuse的原理和如何基于fuse开发一个用户态的文件系统。由于fuse库对操作做了很多封装,因此实现起来并不复杂。后续我们再深入的介绍,包括fuse在内核中的实现和更为详细的用户态实现实例,以期我们对fuse有更加深入的理解。