FUSE介绍



 这里有一篇fuse的官方文档http://fuse.sourceforge.net/

还有一个简单例子:http://fuse.sourceforge.net/helloworld.html

FUSE工作原理如图所示。假设基于FUSE的用户态文件系统hello挂载在/tmp/fuse目录下。当应用层程序要访问/tmp/fuse下的文件时,通过glibc中的函数进行系统调用,处理这些系统调用的VFS中的函数会调用FUSE在内核中的文件系统;内核中的FUSE文件系统将用户的请求,发送给用户态文件系统hello;用户态文件系统收到请求后,进行处理,将结果返回给内核中的FUSE文件系统;最后,内核中的FUSE文件系统将数据返回给用户态程序。

FUSE介绍_第1张图片

其中,用户态文件系统通过fuse提供的函数库libfuse,向内核中的fuse文件系统注册自己的文件处理函数。

我们以一个简单的基于FUSE的用户态文件系统hello为例,来分析FUSE的内部实现。 用 户态文件系统hello的源码hello.c的部分如下所示:

static struct fuse_operations hello_oper = {

.getattr = hello_getattr,

.readdir = hello_readdir,

.open      = hello_open,

.read       = hello_read,

}

int main(int argc, char *argv[])

{

     return  fuse_main(argc, argv, &hello_oper, NULL);

}

fuse_operationslibfuse提供给用户层文件系统的机 构,供用户定义自己的文件操作函数;fuse_main()libfuse提供给用户文件系统最重要的接口,通过这个函数,用户层文件系统可以将自己定义的fuse_operation注册为文件系 统的处理函数,并挂载该文件系统。

1、内核FUSE文件系统和用户态文件系统的通信

在加载fuse模块的过程中,要在内核中注册fuse文件系统,并生成fuse设备/dev/fuse

/dev/fuse是内核里的fuse文件系统和用户态文件系统的通信媒 介。用户态文件系统通过读取/dev/fuse的内容,获取内核中fuse文件系统发来的请求;而内核中的fuse文件系统,则把请求写入/dev/fuse,等待用户态文件系统处理。

在用户态文件系统和内核中的fuse模块通过/dev/fuse进行通信时,不可避免的会有内存拷贝。fuse中没有使用传统的copy_from_user/copy_to_user,而是用get_user_pagesmemcpy来代替。

2、FUSE用户态文件系统的挂载过程

挂载用户态文件系统,只需运行其生成的程序。在hello中,运行编译hello.c生成的hello即可。fuse_main将完成一切的注册和挂载过程,其中主要分为两个步骤:

2.1        在内核中挂载新的用户态文件系统

1.         打开设备文件"/dev/fuse" 获得文件描述符fd

2.         挂载FUSE文件系统(内核里的 fuse文件系统),将fd作为参数传给内核里的挂载函数

3.         内核中的fuse文件系统在初始化 super_block的过程中,将创建一个新的fuse_conn,它就是内核fuse和用户态文件系统通信的工具

4.         将该fuse_conn设置为在用户 态打开的/dev/fuse的私有数据(file->private);同时,该文件系统的super_blocks_fs_info也指向该 fuse_conn

2.2 注册用户态文件系统的处理函数,并创建进程处理该文件系统的请求

1.         创建一个新的fuse_chan,用 来与内核中的fuse通信。它的处理函数是 fuse_chan_ops

 

.receive = fuse_kern_chan_receive, 等待并从4.2.1-1步骤中打开的文件中读取请求

.send = fuse_kern_chan_send,            将用户态文件系统处理的结果发送给内 fuse

.destroy = fuse_kern_chan_destroy, 释放fuse_chan

 

2.         创建一个fuse_session,并 注册用户态文件系统的处理函数,用来等待并处理该文件系统的请求

3.         等待用户态文件系统的请求,并处理它 

 

3、多个基于FUSE的用户态文件系统的共存

在挂载的过程中,每个用户态文件系统 都会创建一个自己的fuse_conn,并将该fuse_conn设为自己打开的/dev/fuse的私有数据(file_private);同时,该文 件系统在内核里的super_block,也将该fuse_conn设置为s_fs_info。在以后的过程中,用户态的文件系统处理线程都是从自己的 fuse_conn上读取请求;内核中的fuse文件系统,也只把请求发给s_fs_info指向的fuse_conn。通过这种机制,FUSE就能支持 多个用户态文件系统同时运行,且互不干扰。

FUSE是一个用户空间中的文件系统框架。它包含2个部分,FUSE内核模块和用户空间接口模块。在用户空间的执行文件系统能够大幅提高生长率,简化了为操作系统提供新的文件系统的工作量,适合于各种虚拟文件系 统和网络文件系统。

FUSE 作为用户态文件系统与内核交互的桥梁,拥有着2个模块。其一是与用户态文件系统直接交互模块,如上图中的libfuse接口。其二是内核中挂载到VFS上的FUSE内核模块。

FUSE信息处理流程:

当用户发起一个请求ls –l /tmp/fuse 时,操作系统调用内核函数接口glibc将该请求发送到内核态VFSVFS根据请求判断出需要调用的文件系统(FUSE已经挂到VFS的文件系统列表上)并将此请求发送到FUSE内核模块;由内核模块再将此请求发 送到特殊文件/dev/fuse的请求队列中;而用户态的libfuse则不停的循环请求/dev/fuse文件中的请求队列,如获得请求则解析该请求,并发送给挂在其上的用户态文件系统,由它来执行请求。

在用户态文件系统执行完该请求后,按照逆方向返回执行结果给用户。

4用户态文件系统的挂载过程

1)用户态文件系统启动的第一部就是将自身注册到VFS上去。首先调用./lib/helper.c文件中函数fuse_mount(const char *mountpoint, struct fuse_args *args), 参数包括挂载点mountpoint等,在挂载过程中只需要输入挂载点路径即可。

2)通过调用./lib/helper.c文件中函数fuse_mount_compat25(const char *mountpoint, struct fuse_args *args) 再调用./lib/mount.c文件中的fuse_kern_mount(const char *mountpoint, struct fuse_args *args),接着调用static int fuse_mount_sys(const char *mnt, struct mount_opts *mo, const char *mnt_opts)函数,其中通过语句 fd = open(devname, O_RDWR);来检测文件/dev/fuse是否能够正常打开。如果正常,则直接调用系统函数res = mount(source, mnt, type, mo->flags, mo->kernel_opts);将其挂载到VFS上。

    3)创建并初始化一个跟/dev/fuse交互的通道结构体

struct fuse_chan {

struct fuse_chan_ops op; //其中操作包括receive, send, distroy三中方法。

struct fuse_session *se;  //通道的会话

int fd;      //文件/dev/fuse fd

size_t bufsize;     //pagesize() + 0x1000

void *data;

int compat;

};

//通道操作的动作绑定结构体

struct fuse_chan_ops op = {

           .receive = fuse_kern_chan_receive,

           .send = fuse_kern_chan_send,

           .destroy = fuse_kern_chan_destroy,

    };

最后将建立的通道fuse_chan返回给用户文件系统的main()函数。

5 FUSE用户态模块的业务逻辑结构

1)        在用户态文件系统的main()函数中调用文件fuse_loop.c中的函数void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch),将通道地址赋值给该会话结构体struct fuse_session se, 然后通过调用同一文件中的函数int fuse_session_loop(struct fuse_session *se)来做信息的接收与发送工作。

struct fuse_session {

       struct fuse_session_ops op;  //会话的操作

       void *data;

       volatile int exited;

       struct fuse_chan *ch;

};

2)  在函数int fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size)中调用结构体struct fuse_chan 中绑定的操作函数static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf, size_t size)直接调用read()系统函数res = read(fuse_chan_fd(ch), buf, size); 来从/dev/fuse中读取消息。

//通道操作 的动作绑定结构体

struct fuse_chan_ops op = {

              .receive = fuse_kern_chan_receive,

              .send = fuse_kern_chan_send,

              .destroy = fuse_kern_chan_destroy,

};

3)   在函数void fuse_session_process(struct fuse_session *se, const char *buf, size_t len, struct fuse_chan *ch)中调用已绑定的se->op.process()函 数转向文件./lib/fuse_lowlevel.c中被绑定函数static void fuse_ll_process(void *data, const char *buf, size_t len, struct fuse_chan *ch)

struct fuse_session_ops sop = {

              .process = fuse_ll_process,

              .destroy = fuse_ll_destroy,

};

在这个函数处理过程中,首先要申明一个请求结构体指针struct fuse_req *req, 然 后通过在read(fuse_chan_fd(ch), buf, size); 函数执行中读取的数据buf,且将其强制转化为fuse_in_header类型(struct fuse_in_header *in = (struct fuse_in_header *) buf;),并初始化请求结构体req\

struct fuse_req {

}

通过解析出in->opcode的数据和用户态文 件系统定义的已绑定的接口函数相比较,得到相应的处理函数,最后执行这个函数完成请求。示例如下:如in->opcode FUSE_MKDIR 则通过结构体

6 FUSE内核模块的业务逻辑结构

FUSE 作为用户态文件系统与内核交互的桥梁,拥有着2个模块。其一是与用户态文件系统直接交互模块,如上图中的libfuse接口。其二是内核中挂载到VFS上的FUSE内核模块。

FUSE信息处理流程:

当用户发起一个请求ls –l /tmp/fuse 时,操作系统调用内核函数接口glibc将该请求发送到内核态VFSVFS根据请求判断出需要调用的文件系统(FUSE已经挂到VFS的文件系统列表上)并将此请求发送到FUSE内核模块;由内核模块再将此请求发 送到特殊文件/dev/fuse的请求队列中;而用户态的libfuse则不停的循环请求/dev/fuse文件中的请求队列,如获得请求则解析该请求,并发送给挂在其上的用户态文件系统,由它来执行请求。

在用户态文件系统执行完该请求后,按照逆方向返回执行结果给用户。

6.1      用户态文件系统的挂载过程

1)        用户态文件系统启动的第一部就是将自身注册 到VFS上去。首 先调用./lib/helper.c文件中函数fuse_mount(const char *mountpoint, struct fuse_args *args), 参数包括挂载点mountpoint等,在挂载过程中只需 要输入挂载点路径即可。

2)        通过调用./lib/helper.c文件中函数fuse_mount_compat25(const char *mountpoint, struct fuse_args *args)再调用./lib/mount.c文件中的fuse_kern_mount(const char *mountpoint, struct fuse_args *args),接着调用static int fuse_mount_sys(const char *mnt, struct mount_opts *mo, const char *mnt_opts)函数,其中通过语句 fd = open(devname, O_RDWR);来检测文件/dev/fuse是否能够正常打开。如果正常,则直接调用系统函数res = mount(source, mnt, type, mo->flags, mo->kernel_opts);将其挂载到VFS上。

3)        创建并初始化一个跟/dev/fuse交互的通道结构体

struct fuse_chan {

struct fuse_chan_ops op; //其中操作包括receive, send, distroy三中方法。

struct fuse_session *se;  //通道的会话

int fd;                //文件/dev/fuse fd

size_t bufsize;         //pagesize() + 0x1000

void *data;

int compat;

};

//通道操作的动作绑定结构体

struct fuse_chan_ops op = {

           .receive = fuse_kern_chan_receive,

           .send = fuse_kern_chan_send,

           .destroy = fuse_kern_chan_destroy,

    };

最后将建立的通道fuse_chan返回给用户文件系统的main()函数。

6.2      FUSE用户态模块的业务逻辑结构

1)        在用户态文件系统的main()函数中调用文件fuse_loop.c中的函数void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch),将通道地址赋值给该会话结构体struct fuse_session se, 然后通过调用同一文件中的函数int fuse_session_loop(struct fuse_session *se)来做信息的接收与发送工作。

struct fuse_session {

       struct fuse_session_ops op;  //会话的操作

       void *data;

       volatile int exited;

       struct fuse_chan *ch;

};

接下来在while()循环中函数int fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size)用于消息的接收函数,如果接收到消息,则调用fuse_session.c文件中void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,处理函数进行处理,其中调用会话控制结构体中的process函数指针所指向的函数来做实 际的处理(这个函数是由用户态文件系统提供的文件操作函数接口)。

struct fuse_session_ops {

void (*process) (void *data, const char *buf, size_t len,  struct fuse_chan *ch);

void (*exit) (void *data, int val);

int (*exited) (void *data);

void (*destroy) (void *data);

};

1)        在函数int fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size)中调用结构体struct fuse_chan中绑定的 操作函数static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf, size_t size)直接调用read()系统函数res = read(fuse_chan_fd(ch), buf, size); 来从/dev/fuse中读取消息。

//通道操作的动作绑定结构体

struct fuse_chan_ops op = {

              .receive = fuse_kern_chan_receive,

              .send = fuse_kern_chan_send,

              .destroy = fuse_kern_chan_destroy,

};

1)        在函数void fuse_session_process(struct fuse_session *se, const char *buf, size_t len, struct fuse_chan *ch)中 调用已绑定的se->op.process()函数转向文件./lib/fuse_lowlevel.c中被绑定函数static void fuse_ll_process(void *data, const char *buf, size_t len, struct fuse_chan *ch)

struct fuse_session_ops sop = {

              .process = fuse_ll_process,

              .destroy = fuse_ll_destroy,

};

在这个函数处理过程中,首先要申明一个请求结构体指针struct fuse_req *req, 然后通过在read(fuse_chan_fd(ch), buf, size); 

你可能感兴趣的:(Linux)