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

其中,用户态文件系统通过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_operations是libfuse提供给用户层文件系统的机 构,供用户定义自己的文件操作函数;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_pages和memcpy来代替。
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_block的s_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将该请求发送到内核态VFS;VFS根据请求判断出需要调用的文件系统(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的数据和用户态文件系统定义的已绑定的接口函数相比较,得到相应的处理函数,最后执行这个函数完成请求。

2017.7.11 fuse工作原理的更多相关文章

  1. 基于C++11实现线程池的工作原理

    目录 基于C++11实现线程池的工作原理. 简介 线程池的组成 1.线程池管理器 2.工作线程 3.任务接口, 4.任务队列 线程池工作的四种情况. 1.主程序当前没有任务要执行,线程池中的任务队列为 ...

  2. 11.深入k8s:kubelet工作原理及源码分析

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 源码版本是1.19 kubelet信息量是很大的,通过我这一篇文章肯定是讲不全的,大家可 ...

  3. 2017.10.24 Java 详解 JVM 工作原理和流程

    JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.exe来完成,通过下面4步来完成JVM环境. 1.创建JVM装载环境和配置 2.装载JVM.dll 3.初始化JVM.dll并挂界 ...

  4. 《浏览器工作原理与实践》<11>this:从JavaScript执行上下文的视角讲清楚this

    在上篇文章中,我们讲了词法作用域.作用域链以及闭包,接下来我们分析一下这段代码: var bar = { myName:"time.geekbang.com", printName ...

  5. CentOS6下DHCP服务(一)工作原理及安装配置说明

    1.DHCP服务用途 DHCP是Dynamic Host Configuration Protocol的简写,DHCP服务器最主要的工作就是自动地将网络参数分配给网络中的每台计算机,让客户端的计算机在 ...

  6. How Javascript works (Javascript工作原理) (五) 深入理解 WebSockets 和带有 SSE 机制的HTTP/2 以及正确的使用姿势

    个人总结: 1.长连接机制——分清Websocket,http2,SSE: 1)HTTP/2 引进了 Server Push 技术用来让服务器主动向客户端缓存发送数据.然而,它并不允许直接向客户端程序 ...

  7. FIR滤波器工作原理(算法)以及verilog算法实现(包含与IIR的一些对比)

    滤波器在2017年IC前端的笔试中,出现频率十分的高.不论今后是否会涉及,还是要记住一些会比较好.接下来就将从这四个方面来讲解,FIR数字滤波器的工作原理(算法)与verilog实现. ·什么是FIR ...

  8. 【夯实Nginx基础】Nginx工作原理和优化、漏洞

    本文地址 原文地址 本文提纲: 1.  Nginx的模块与工作原理    2.  Nginx的进程模型    3 . NginxFastCGI运行原理        3.1 什么是 FastCGI   ...

  9. 【Oracle 集群】ORACLE DATABASE 11G RAC 知识图文详细教程之RAC 工作原理和相关组件(三)

    RAC 工作原理和相关组件(三) 概述:写下本文档的初衷和动力,来源于上篇的<oracle基本操作手册>.oracle基本操作手册是作者研一假期对oracle基础知识学习的汇总.然后形成体 ...

随机推荐

  1. 从概率图模型pgm到rbm

    有向图模型:directed acyclic graph  DAG  贝叶斯网络 对称的,无向图, UGM: undirected graphic model  UGM, 更有名的名称是MRF,mar ...

  2. Linux第二周作业

    通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的 1.进入vi编写C语言程序代码,首先必须输入命令vi main,c,其中main.c是文件名. 紧接着按esc键退出编辑状态,再输入一个 ...

  3. WPF客户端实现.net升级

    客户端.net版本由3.5升级到4.5,首先把.net4.5的离线安装包添加到资源,程序运行的时候,从资源中生成离线安装包,并通过传递参数的方式执行静默安装命令,具体代码如下: private sta ...

  4. Java Web(一) 前言及体系结构

    Web应用程序 Web程序是什么 Web应用程序就是一般所说的网站,由服务器,客户端浏览器以及网络组成.但Web程序又不是一般意义的网站,一般的网站是提供信息服务,重在内容,程序往往比较简单,但商用的 ...

  5. JS-封装类或对象的最佳方案

    JS封装类或对象的最佳方案 面向对象强大的优点之一是能够创建自己专用的类或者对象,封装一组属性和行为.抛开性能来说,JS要比面向对象语言如JAVA要灵活好用的多,组装数据结构很灵活方便.那么我们如何来 ...

  6. 51nod1339飞行任务

    首先按照收获从大到小排序. 然后01背包取或者不取即可. 至于为什么这样对的其实我也不知道.... 代码: #include<bits/stdc++.h> using namespace ...

  7. bootstrap table 分页显示问题

    1.bootstrap-table客户端分页 客户端分页的数据源可以是后台服务器端传递过来(一次性获取,即获取所有你需要的数据),点击页码不再请求后台,利用页面缓存分页;cache : true, / ...

  8. DevExpress ASP.NET v18.2新功能详解(一)

    行业领先的.NET界面控件2018年第二次重大更新——DevExpress v18.2日前正式发布,本站将以连载的形式为大家介绍新版本新功能.本文将介绍了DevExpress ASP.NET Cont ...

  9. jquery 正则表达式

  10. L321 How Technology Is Revolutionizing Health Care

    How Technology Is Revolutionizing Health Care One of technology’s biggest potential impacts on healt ...