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. nyoj-0737-石子合并(dp)

    nyoj-0737-石子合并 题意:有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并 ...

  2. AI工具(缩放工具)(路径查找器)(旋转工具)(镜像工具)5.12

    缩放工具:选择图形,点击工具栏缩放工具,”ENTER"输入缩放比例,“复制”完成缩放复制. 路径查找器:选择两个图形,“窗口”“路径查找器”“差集”:大圆减去小圆,留下圆环. “减去顶层”: ...

  3. shell 基本概述

    SHELL的概念 SHELL是一个命令行解释器,它为用户提供了一个向Linux内核发送请求以便运行程序的界面系统级程序, 用户可以用shell来启动,挂起,停止甚至是编写一些程序. ​ Shell还是 ...

  4. prototype:构造函数的真相、原型链

    函数不是构造函数,但是当且仅当使用 new 时,函数调用会变成 ‘构造函数调用’.那么对 ’构造函数‘ 最准确的解释是:所有带 new 的函数调用. Nothing 只是一个普通的函数,但使用 new ...

  5. js 操作dom

    childNodes 返回当前元素所有子元素的数组 parentNode 返回元素的父节点 document.createElement(tagName) 文档对象上的createElement方法可 ...

  6. Date和Timestamp区别

    主要是精度问题,date没有ms,而timestamp是有ms的,所以date的精度要低于timestamp. 而且二者可以互相转换. 除此之外,没有什么不同,

  7. Java的file类

    package IO; import java.io.File; import java.io.IOException; public class FileDemo01 { public static ...

  8. python中变量命名的基本规则,标识符和关键字

    变量的命名 目标 标识符和关键字 变量的命名规则 0.1 标识符和关键字 1.1 标识符 标示符就是程序员定义的 变量名.函数名 名字 需要有 见名知义 的效果,见下图:  标示符可以由 字母.下划 ...

  9. 手机号的 DES-ECB 加密/解密

    前言:公司的手机号加密更换了加密方法,这次改成 DES-ECB 加密了 代码操作 # -*- coding:utf-8 -*- import base64 import json from Crypt ...

  10. pytest的HTML

    安装html: 1.github上源码地址[https://github.com/pytest-dev/pytest-html] 2.pip安装  pip install pytest-html 3. ...