文件系统小册(FusePosixK8s csi)【1 Fuse】

文件系统小册(Fuse&Posix&K8s csi)【1 Fuse:用户空间的文件系统】

Fuse(filesystem in userspace),是一个用户空间的文件系统。通过fuse内核模块的支持,开发者只需要根据fuse提供的接口实现具体的文件操作就可以实现一个文件系统。由于其主要实现代码位于用户空间中,而不需要重新编译内核,这给开发者带来了众多便利。

  • 虽然Fuse简化了文件系统的实现,给开发者带来了便利。但是其额外的内核态/用户态切换带来的性能开销不能被忽视,所以fuse性能问题,一直是业界绕不开的话题。下面说到的splice、多线程、writeback cache都是为了改善其性能问题。

1 架构设计(执行流程)

  1. 用户程序挂载到fuse文件系统,比如此时执行ls命令
  2. VFS(虚拟文件系统)检测到挂载到fuse文件系统上的用户程序发送的请求,会将其转发给fuse driver
  3. fuse driver接受到request请求,会将其保存到queue中,同时暂停用户程序(ls会卡主,等待返回结果),同时唤醒fuse daemon处理请求
  4. fuse daemon(守护进程)通过/dev/fuse读取queue中的request,经过处理后将其转发给内核底层文件系统(EXT4等)。
  5. 内核文件系统处理完成后将结果返回给fuse daemon,fuse daemon将结果写回/dev/fuse
  6. fuse driver将该request标记为completed,并唤醒用户进程,返回对应执行结果。(ls执行结束,终端展示文件列表)
    在这里插入图片描述

2 相关组件

①VFS:转发请求给fuse driver

VFS(虚拟文件系统)检测到挂载到fuse文件系统上的用户程序发送的请求,会将其转发给fuse driver

② FUSE drvier(queue):接受请求保存到queue

fuse driver接受到request请求,会将其保存到queue中,同时暂停用户程序(ls会卡主,等待返回结果)

③/dev/fuse(桥梁):fuse daemon通过/dev/fuse读取queue中的请求

FUSE 驱动程序(fuse driver)处理请求并将其加入队列,然后通过 /dev/fuse 文件(FUSE 守护程序无法读取该文件)中的特定连接实例将请求提交给负责处理该 FUSE 文件系统的 FUSE 守护程序。

  • fuse daemon通过/dev/fuse来读取request queue中的请求

④fuse daemon(中间人):从queue中读取请求转发给底层文件系统

fuse daemon(守护进程)通过/dev/fuse读取queue中的request,经过处理后将其转发给内核底层文件系统(EXT4等)。

⑤fuse lib:提供接口和内核fuse模块通信

fuse的lib库,封装好了对应接口。fuse的lib库,提供接口和内核fus模块通信

⑥内核文件系统(如:EXT4)

内核层面的文件系统,真正操作文件的系统。

3 实现细节

① fuse用户空间流程

1. fuse mount:通过mount函数将path挂载到/dev/fuse设备

Fuse的挂载通过mount函数,将指定的fuse_path挂载到/dev/fuse设备上。之后对于fuse_path下的文件操作,都会通过fuse文件系统,并通过/dev/fuse被fuse daemon读取处理。

在这里插入图片描述

2. fuse thread:fuse daemon创建的服务线程

Fuse daemon还会创建一个服务线程,基于libfuse库来处理文件操作请求。这里主要关注fuse_session_new和fuse_session_loop_mt。通过fuse_session_new在libfuse中注册了fuse daemon实现的fuse_lowlevel_ops,之后通过fuse的所有的文件操作,都会通过libfuse回调到fuse daemon进行处理。fuse_session_loop_mt在libfuse中实现了一个多线程模式来读取请求,相比单线程,在请求处理上效率更高。

  • fuse daemon创建的服务线程
  • 基于libfuse库处理请求
  • 可多线程模式
  • 通过fuse_session_new(new一个session,与内核fuse模块通信)+fuse_session_loop_mt(多线程处理请求)

在这里插入图片描述

3. libfuse:fuse的lib库,提供接口和内核fus模块通信

fuse_session_loop_mt:fuse thread基于多线程方式处理请求

  • splice实现内存零拷贝。在默认情况下,fuse daemon必须通过read()从/dev/fuse读取请求,通过write()将请求回复写入/dev/fuse。每次读写系统调用都需要进行一次内核-用户空间的内存拷贝。这样对读写的性能损耗十分严重,因为一次内存拷贝需要处理大量数据。为了缓解这个问题,fuse支持了Linux内核提供的 splice 功能。splice 允许用户空间在两个内核内存缓冲区之间传输数据,而无需将数据复制给用户空间。如果fuse daemon实现了write_buf()方法,则 FUSE 从/dev/fuse读取数据,并以包含文件描述符的缓冲区的形式将数据直接传递给此方法处理,从而省去了一次内存申请与拷贝。[提供缓冲区传数据,避免用户空间与内核空间来回切换耗时]
  • 多线程模式。在多线程模式下,fuse daemon以一个线程开始,如果内核队列中有两个以上的request,则会自动生成其他线程。默认最大支持10个线程同时处理请求。 [多线程:队列request>2,自动生成新线程,最大支持10并发]在这里插入图片描述

②fuse内核队列(维护了5个队列)

fuse在内核中维护了五个队列,分别为:Backgroud、Pending、Processing、Interrupts、Forgets。一个请求在任何时候只会存在于一个队列中。

  • Backgroud:存异步请求
  • Pending:存同步请求
  • Processing:存处理中的请求
  • Interrupts:存中断请求(如:用户ctrl+C,取消请求),优先级最高
  • Forgets:存forget请求(清理cache中的inode)

在这里插入图片描述

1. Backgroud:暂存异步请求

Backgroud:background 队列用于暂存异步请求。在默认情况下,只有读请求进入 background 队列;当writeback cache启用时,写请求也会进入 background 队列。当开启writeback cache时,来自用户进程的写请求会先在页缓存中累积,然后当bdflush 线程被唤醒时会下刷脏页。在下刷脏页时,FUSE会构造异步请求,并将它们放入 background 队列中。

2. Pending:存储同步请求

同步请求(例如,元数据)放在 pending 队列中,并且pending队列会周期性接收来自background 的请求。但是pending队列中异步请求的个数最大为max_background(最大为12),当pending队列的异步请求未达到12时,background队列的请求将被移动到pending队列中。这样做的目的是为了控制pending队列中异步请求的个数,防止在突发大量异步请求的情况下,阻塞了同步请求。

3. Processing:存储正在处理的请求

Processing:当pending队列中的请求被转发到fuse daemon的同时,也被移动到processing队列。所以processing队列中的请求,表示正在被处理fuse daemon处理的请求。当fuse daemon真正处理完请求,通过/dev/fuse下发reply时,该请求将从processing队列中删除。

4. Interrupts:存放中断请求(用户取消请求:如:ctrl+C)

Interrupts:用于存放中断请求,比如当发送的请求被用户取消时,内核会发送一个Interrupts请求,来取消已被发送的请求。中断请求的优先级最高,Interrupts中的请求会最先得到处理。

5. Forgets:记录清理cache中inode的请求

Forgets:存储forgets请求,forget请求用于删除cache中缓存的inode。

③/dev/fuse 读写调用流程

Fuse driver加载过程中注册了对/dev/fuse的操作接口fuse_dev_operations。fuse_dev_do_read/fuse_dev_do_write分别对应fuse daemon从内核读取请求,以及处理完请求后写回reply的函数调用。

  1. pending 、interrups、forgets队列为空时,读进程休眠。
  2. 一旦有request到达,对应等待队列上的进程被唤醒(Interrups 和 forgets优先级高于pending队列请求)
  3. 当请求数据内容被拷贝到用户空间后(fuse daemon在进行处理了)
  4. 该请求被移动到processing队列,标识该请求已被处理。
  5. req->flags会保存当前请求的状态
  6. fuse daemon处理完请求后(fuse daemon与内核底层FS打交道)
  7. fuse daemon将结果写回到/dev/fuse。
  • 其中写数据保存在struct fuse_copy_state中,并且会根据unique id在fc(fuse_conn)中找到对应的req,并将写回的参数从fuse_copy_state拷贝至req->out。

源码逻辑:

当pending 、interrups、forgets队列都没有请求时,读进程进入休眠。一旦有请求到达,这个等待队列上的进程将被唤醒。Interrups 和 forgets的请求优先级高于pending队列。当请求的数据内容被拷贝至用户空间后,该请求会被移至processing队列,并且req->flags会保存当前请求的状态。

在这里插入图片描述

当fuse daemon处理完请求后,会将结果写回到/dev/fuse。写数据保存在struct fuse_copy_state中,并且会根据unique id在fc(fuse_conn)中找到对应的req,并将写回的参数从fuse_copy_state拷贝至req->out。

在这里插入图片描述

4案例:以unlink为例

  1. fuse daemon会阻塞在读/dev/fuse,当app进程在fuse挂载点下面有新的文件操作(unlink)
  2. 这时系统调用会调用fuse内核接口,并生成request,同时唤醒阻塞的fuse daemon
  3. fuse daemon读到request后,在libfuse中进行解析,根据request的opcode来执行对应的ops
  4. 完成后会把处理结果返回给/dev/fuse。此时vfs调用阻塞的行为将被唤醒,最后返回vfs调用。

在这里插入图片描述

5 实战(go-fuse)

相关仓库地址:

  • https://github.com/hanwen/go-fuse
  • https://github.com/bazil/fuse
  • https://github.com/libfuse/libfuse/

Golang操作fuse的库主要有go-fuse、libfuse。这里主要讲解go-fuse

①概述

Go-Fuse 是一个开源的库,由 Han-Wen Nienhuys 创建并维护。该库提供了对 Linux FUSE(Filesystem in Userspace)接口的支持,使得开发人员可以使用 Go 语言构建自己的文件系统。
功能:

  • 构建自定义文件系统:使用 Go-Fuse,您可以根据需要构建自己的文件系统。这可能包括加密、压缩、优化性能等功能。
  • 支持各种平台:由于 Go-Fuse 基于 FUSE,因此它可以跨多个操作系统(如 Linux、macOS 和 Windows)运行。
  • 高度自定义:通过实现特定的接口方法,您可以控制文件系统的每个细节。这为实现复杂的文件系统行为提供了极大的灵活性。

②环境准备

我准备在我本地macos上构建,因此需要fuse命令。

  • macos:https://github.com/osxfuse/osxfuse/releases(下载dmg安装配置)
  • ubuntu: sudo apt-get -y update && sudo apt-get install -y fuse
  • centos:sudo yum -y update && sudo yum install -y fuse

安装好之后,需要确保当前用户需要有执行fuse命令的权限

# 如果当前用户没有权限,可以进行提权或者切换用户,或者修改fuse配置
vim /etc/fuse.conf

打开user_allow_other

③全部代码&解析

//安装依赖
go get "github.com/hanwen/go-fuse/v2/fs"
go get "github.com/hanwen/go-fuse/v2/fuse"
package main

import (
	"context"
	"flag"
	"log"
	"syscall"

	"github.com/hanwen/go-fuse/v2/fs"
	"github.com/hanwen/go-fuse/v2/fuse"
)

type HelloRoot struct {
	fs.Inode
}

func (r *HelloRoot) OnAdd(ctx context.Context) {
	ch := r.NewPersistentInode(
		ctx, &fs.MemRegularFile{
			Data: []byte("file.txt data"),
			Attr: fuse.Attr{
				Mode: 0644,
			},
		}, fs.StableAttr{Ino: 2})
	r.AddChild("file.txt", ch, false)
}

func (r *HelloRoot) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
	out.Mode = 0755
	return 0
}

var _ = (fs.NodeGetattrer)((*HelloRoot)(nil))
var _ = (fs.NodeOnAdder)((*HelloRoot)(nil))

//./yi-fuse test
func main() {
	debug := flag.Bool("debug", false, "print debug data")
	flag.Parse()
	if len(flag.Args()) < 1 {
		log.Fatal("Usage:\n  ./yi-fuse MOUNTPOINT")
	}
	opts := &fs.Options{}
	opts.Debug = *debug
	server, err := fs.Mount(flag.Arg(0), &HelloRoot{}, opts)
	if err != nil {
		log.Fatalf("Mount fail: %v\n", err)
	}
	server.Wait()
}
  • 我们通过go-fuse库创建了一个用户空间文件系统,该文件系统只包含一个名为file.txt的文件。
  • context:用于处理上下文,可以在异步操作中取消请求。
  • flag:处理命令行参数。
  • log:日志记录。
  • syscall:系统调用接口。
  • fs 和 fuse:来自github.com/hanwen/go-fuse/v2的库,用于实现用户空间文件系统。
  • HelloRoot 结构体:
    • 表示文件系统的根节点,实现了NodeGetattrer和NodeOnAdder接口。
    • OnAdd 方法:当文件系统被加载时调用,创建一个包含file.txt的持久化节点。
    • Getattr 方法:获取文件属性,将file.txt的权限设置为0755。
    • main 函数:
      处理命令行参数,设置调试标志。
      检查至少有一个挂载点参数。
      创建fs.Options,启用调试模式。
      调用fs.Mount挂载文件系统。
      如果挂载失败,打印错误信息并退出。
  • server.Wait()阻塞直到文件系统卸载。

④测试

//编译可执行文件到linux
GOOS=linux GOARCH=amd64 go build -o yi-fuse main.go 
//创建挂载目录
mkdir -p /root/test
//执行挂载(如果不加nohup,默认前台运行)
nohup ./yi-fuse /root/test &

//预期返回我们代码里写的file.txt文件
ls -l /root/test

//读取file.txt文件内容
cat /root/test/file.txt

//卸载挂载
umount /root/test

在这里插入图片描述

参考文章:
https://www.cnblogs.com/Linux-tech/p/14110335.html
https://blog.csdn.net/gitblog_00007/article/details/136569849

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/666641.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity中的MVC框架

基本概念 MVC全名是Model View Controller 是模型(model)-视图(view)-控制器(controller)的缩写 是一种软件设计规范&#xff0c;用一种业务逻辑、数据、界面显示 分离的方法组织代码 将业务逻辑聚集到一个部件里面&#xff0c;在改进和个性化定制界面及用户交互的同时&#x…

tinycudann安装

在安装完torch等 直接运行下面的指令会出现错误 pip install githttps://github.com/NVlabs/tiny-cuda-nn/#subdirectorybindings/torch大部分错误是下面的 大概看了一下都是因为虚拟环境里面的include文件下缺少文件&#xff0c;将之前的一些.h文件全部复制过来在执行上面的…

TransmittableThreadLocal原理

1、原理 TransmittableThreadLocal&#xff08;简称TTL&#xff09;是阿里巴巴开源的一个Java库&#xff0c;用于解决线程池中线程本地变量传递的问题。其底层原理主要是基于Java的ThreadLocal机制并对其进行扩展&#xff0c;以支持在父子线程间以及线程池中任务切换时&#x…

Java实现顺序表

Java顺序表 前言一、线性表介绍常见线性表总结图解 二、顺序表概念顺序表的分类顺序表的实现throw具体代码 三、顺序表会出现的问题 前言 推荐一个网站给想要了解或者学习人工智能知识的读者&#xff0c;这个网站里内容讲解通俗易懂且风趣幽默&#xff0c;对我帮助很大。我想与…

Adobe AntiCC 简化版 安装教程

Adobe AntiCC 简化版 安装教程 原文地址&#xff1a;https://blog.csdn.net/weixin_48311847/article/details/139277743

TensorFlow Playground神经网络演示工具使用方法详解

在现代机器学习领域,神经网络无疑是一个重要的研究方向。然而,对于许多初学者来说,神经网络的概念和实际操作可能显得相当复杂。幸运的是,TensorFlow Playground 提供了一个交互式的在线工具,使得我们可以直观地理解和实验神经网络的基本原理。在这篇博客中,我们将详细介…

解读vue3源码-2

提示&#xff1a;看到我 请让滚去学习 vue3编译模版的提升 文章目录 vue3编译模版的提升静态节点提升补丁标志和block的使用附录&#xff1a; template explorer可以将我们的源模版转化成渲染函数代码&#xff0c;vue2中就有&#xff0c;而Vue3 template explorer 功能更加丰富…

开发nfc读卡器应用出现报错Unhandled Exception: SCARD_E_NO_SERVICE

使用flutter开发ACR122U的nfc读卡器的时候&#xff0c;报错&#xff1a; [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Exception: Error while establing context. Reason: SCARD_E_NO_SERVICE #0 PCSCBinding._checkAndThrow (package:fl…

Vue 框选区域放大(纯JavaScript实现)

需求&#xff1a;长按鼠标左键框选区域&#xff0c;松开后放大该区域&#xff0c;继续框选继续放大&#xff0c;反向框选恢复原始状态 实现思路&#xff1a;根据鼠标的落点&#xff0c;放大要显示的内容&#xff08;内层盒子&#xff09;&#xff0c;然后利用水平偏移和垂直偏…

ABB码垛机器人IRB260通讯板维修

ABB码垛机器人在现代制造业中发挥着重要作用&#xff0c;而机器人通讯板维修对于确保机器人的正常运行至关重要。 通讯板是ABB码垛机器人与控制系统之间进行数据传输的桥梁。它负责接收控制系统的指令&#xff0c;并将机器人的运行数据反馈给控制系统。如果通讯板出现故障&…

ESP32开发板定义硬串口

ESP32 的默认串口 UART序号Rx PINTx PIN是否可用UART0GPIO3GPIO1是UART1GPIO9GPIO10是&#xff0c; 但与SPI flash相关联需要重新定义UART2GPIO16GPIO17是 下面我们定义2、4GPIO引脚为串口1&#xff1a; #include <HardwareSerial.h> HardwareSerial S1(1); 初始化 …

C语言 | Leetcode C语言题解之第120题三角形最小路径和

题目&#xff1a; 题解&#xff1a; int minimumTotal(int** triangle, int triangleSize, int* triangleColSize) {int f[triangleSize];memset(f, 0, sizeof(f));f[0] triangle[0][0];for (int i 1; i < triangleSize; i) {f[i] f[i - 1] triangle[i][i];for (int j …

【VTKExamples::PolyData】第五十四期 SelectVisiblePoints

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 公众号:VTK忠粉 前言 本文分享VTK样例SelectVisiblePoints,并解析接口vtkSelectVisiblePoints,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动…

[Linux系统编程]文件IO

一.系统调用 什么是系统调用? 只有系统调用(系统函数)才能进入内核空间&#xff0c;库函数也是调用系统函数&#xff0c;才得以访问底层。 系统调用由操作系统实现并提供给外部应用程序的编程接口。是应用程序同系统之间数据交互的桥梁。 换句话说&#xff0c;系统调用就是操…

yolov5-ros模型结合zed2相机部署在 Ubuntu系统

前言 本篇文章主要讲解yolov5-ros模型结合zed2相机进行实时检测&#xff0c;经改进实现了红绿灯检测&#xff0c;并输出检测类别与置信度&#xff01; 目录 一、环境配置二、zed2驱动安装三、yolov5-ros功能包配置四、运行官方权重文件四、运行自己权重文件 一、环境配置 1、…

14-alert\confirm\prompt\自定义弹窗

一、认识alert\confirm\prompt 下图依次是alert、confirm、prompt&#xff0c;先认清楚长什么样子&#xff0c;以后遇到了就知道如何操作了。 二、alert操作 先用driver.switch_to.alert方法切换到alert弹出框上&#xff1b;可以用text方法获取弹出的文本信息&#xff1b;acce…

【介绍下运维,什么是运维?】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

C++ | Leetcode C++题解之第120题三角形最小路径和

题目&#xff1a; 题解&#xff1a; class Solution { public:int minimumTotal(vector<vector<int>>& triangle) {int n triangle.size();vector<int> f(n);f[0] triangle[0][0];for (int i 1; i < n; i) {f[i] f[i - 1] triangle[i][i];for (…

RK3588+FPGA+AI高性能边缘计算盒子,应用于视频分析、图像视觉等

搭载RK3588&#xff08;四核 A76四核 A55&#xff09;&#xff0c;CPU主频高达 2.4GHz &#xff0c;提供1MB L2 Cache 和 3MB L3 &#xff0c;Cache提供更强的 CPU运算能力&#xff0c;具备6T AI算力&#xff0c;可扩展至38T算力。 产品规格 系统主控CPURK3588&#xff0c;四核…

【QEMU中文文档】1.1 支持的构建平台

本文由 AI 翻译&#xff08;ChatGPT-4&#xff09;完成&#xff0c;并由作者进行人工校对。如有任何问题或建议&#xff0c;欢迎联系我。联系方式&#xff1a;jelin-shoutlook.com。 原文&#xff1a;Supported build platforms — QEMU documentation QEMU 旨在支持在多个主机…