文件系统小册(FusePosixK8s csi)【1 Fuse】
文件系统小册(Fuse&Posix&K8s csi)【1 Fuse:用户空间的文件系统】
Fuse(filesystem in userspace),是一个用户空间的文件系统。通过fuse内核模块的支持,开发者只需要根据fuse提供的接口实现具体的文件操作就可以实现一个文件系统。由于其主要实现代码位于用户空间中,而不需要重新编译内核,这给开发者带来了众多便利。
- 虽然Fuse简化了文件系统的实现,给开发者带来了便利。但是其额外的内核态/用户态切换带来的性能开销不能被忽视,所以fuse性能问题,一直是业界绕不开的话题。下面说到的splice、多线程、writeback cache都是为了改善其性能问题。
1 架构设计(执行流程)
- 用户程序挂载到fuse文件系统,比如此时执行ls命令
- VFS(虚拟文件系统)检测到挂载到fuse文件系统上的用户程序发送的请求,会将其转发给fuse driver
- fuse driver接受到request请求,会将其保存到queue中,同时暂停用户程序(ls会卡主,等待返回结果),同时唤醒fuse daemon处理请求
- fuse daemon(守护进程)通过/dev/fuse读取queue中的request,经过处理后将其转发给内核底层文件系统(EXT4等)。
- 内核文件系统处理完成后将结果返回给fuse daemon,fuse daemon将结果写回/dev/fuse
- 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的函数调用。
- pending 、interrups、forgets队列为空时,读进程休眠。
- 一旦有request到达,对应等待队列上的进程被唤醒(Interrups 和 forgets优先级高于pending队列请求)
- 当请求数据内容被拷贝到用户空间后(fuse daemon在进行处理了)
- 该请求被移动到processing队列,标识该请求已被处理。
- req->flags会保存当前请求的状态
- fuse daemon处理完请求后(fuse daemon与内核底层FS打交道)
- 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为例
- fuse daemon会阻塞在读/dev/fuse,当app进程在fuse挂载点下面有新的文件操作(unlink)
- 这时系统调用会调用fuse内核接口,并生成request,同时唤醒阻塞的fuse daemon
- fuse daemon读到request后,在libfuse中进行解析,根据request的opcode来执行对应的ops
- 完成后会把处理结果返回给/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 mainimport ("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 = 0755return 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 = *debugserver, 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
相关文章:

文件系统小册(FusePosixK8s csi)【1 Fuse】
文件系统小册(Fuse&Posix&K8s csi)【1 Fuse:用户空间的文件系统】 Fuse(filesystem in userspace),是一个用户空间的文件系统。通过fuse内核模块的支持,开发者只需要根据fuse提供的接口实现具体的文件操作就可以实现一个文…...
Bootstrap 环境安装
Bootstrap 环境安装 Bootstrap 是一个流行的前端框架,用于快速开发响应式和移动设备优先的网站。在开始使用 Bootstrap 之前,您需要安装相应的环境。本文将指导您如何安装 Bootstrap 环境。 1. 环境要求 在开始之前,请确保您的计算机上已安装以下软件: Node.js:Bootstr…...

GWT 与 Python App Engine 集成
将 Google Web Toolkit (GWT) 与 Python App Engine 集成可以实现强大的 Web 应用程序开发。这种集成允许你使用 GWT 的 Java 客户端技术构建丰富的用户界面,并将其与 Python 后端结合在一起,后端可以运行在 Google App Engine 上。 1、问题背景 在 Pyt…...

golang的函数为什么能有多个返回值?
在golang1.17之前,函数的参数和返回值都是放在函数栈里面的,比如函数A调用函数B,那么B的实参和返回值都是存放在函数A的栈里面,所以可以轻松的返回多个值。 其他的编程语言大都使用某个寄存器来存储函数的返回值。 但是从golang…...

一次 K8s 故障诊断:从 CPU 高负载到存储挂载泄露根源揭示
一、背景 现代软件部署中,容器技术已成为不可或缺的一环,在云计算和微服务架构中发挥着核心作用。随着容器化应用的普及,确保容器环境的可靠性成为了一个至关重要的任务。这就是容器SRE(Site Reliability Engineering,…...
python大作业:实现的简易股票简易系统(含源码、说明和运行截图)
实现一个简单的股票交易模拟系统。该系统将包括以下几个部分: 数据处理:从CSV文件中读取股票数据。 股票交易算法:实现一个简单的交易策略。 命令行界面(CLI):允许用户查看股票数据和进行交易。 数据持久化:将用户的交易记录和当前资金存储在数据库中。 为了简化这个示例…...
python-NLP常用数据集0.1.012
XNLI数据集 用户语言翻译和跨语言分类的语料库 官网地址:https://github.com/facebookresearch/XNLI下载地址:https://dl.fbaipublicfiles.com/XNLI/XNLI-1.0.zip注意事项:数据集有json格式的,和txt格式的数据格式 txt格式 la…...

【大事件】docker可能无法使用了
今天本想继续学习docker的命令,突然发现官方网站的文档页面打不开了。 难道是被墙了? 我用同事的翻了一下,能进,果然! 正好手头的工作告一段落,将代码上传,然后通过jenkins将服务器自动部署到…...
探索Linux中的gzip命令:压缩与解压缩的艺术
探索Linux中的gzip命令:压缩与解压缩的艺术 在Linux世界中,文件压缩和解压缩是日常任务中不可或缺的一部分。gzip命令是这些任务中的佼佼者,它提供了高效的压缩和解压缩功能,广泛应用于各种场景。本文将带您深入了解gzip命令的工…...
Shell 输入/输出重定向
💝💝💝首先,欢迎各位来到我的博客,很高兴能够在这里和您见面!希望您在这里不仅可以有所收获,同时也能感受到一份轻松欢乐的氛围,祝你生活愉快! 💝Ὁ…...
为什么RPC要比Http高效?
RPC和HTTP RPC(Remote Procedure Call)基于TCP连接通常比HTTP在性能上要高很多,原因如下: 1. 协议开销 HTTP开销: HTTP协议报文头部相对较大,包含大量的元数据(如方法、URI、头字段等&#x…...
局域网电脑监控软件是如何监控到内网电脑的?
在信息化快速发展的今天,局域网电脑监控软件成为许多企业、学校和机构重要的实用工具。这些软件的主要功能在于对局域网内的电脑进行实时监控,以确保网络的安全、员工的工作效率以及合规性。那么,局域网电脑监控软件是如何做到对内网电脑进行…...

精妙无比的App UI 风格
精妙无比的App UI 风格...

SQL优化系列-快速学会分析SQL执行效率(下)
1 show profile 分析慢查询 有时需要确定 SQL 到底慢在哪个环节,此时 explain 可能不好确定。在 MySQL 数据库中,通过 profile,能够更清楚地了解 SQL 执行过程的资源使用情况,能让我们知道到底慢在哪个环节。 知识扩展࿱…...

交流非线性RCD负载的核心功能
非线性RCD负载是一种广泛应用于电力系统中的电子元件,主要用于保护电路免受过电压和欠电压的影响。它的核心功能主要包括以下几个方面: 1. 过电压保护:当电路中的电压超过设定值时,非线性RCD负载会自动断开电路,防止电…...

英语学习笔记31——Where‘s Sally?
Where’s Sally? Sally在哪? 词汇 Vocabulary garden /ˈɡɑːrdn/ n. 花园,院子(属于私人) 区别:park n. 公园(公共的) 例句:我的花园非常大。 My garden is very big. 搭…...

【Unity脚本】使用脚本操作游戏对象的组件
【知识链】Unity -> Unity脚本 -> 游戏对象 -> 组件 【知识链】Unity -> Unity界面 -> Inspector【摘要】本文介绍如何使用脚本添加、删除组件,以及如何访问组件 文章目录 引言第一章 游戏对象与组件1.1 什么是组件?1.2 场景、游戏对象与…...
学习VUE3——组件(一)
组件注册 分为全局注册和局部注册两种。 全局注册: 在main.js或main.ts中,使用 Vue 应用实例的 .component() 方法,让组件在当前 Vue 应用中全局可用。 import { createApp } from vue import MyComponent from ./App.vueconst app crea…...

2024-6-6 石群电路-25
2024-6-6,星期四,15:56,天气:晴,心情:晴。今天又是阳光明媚的一天打印了毕业论文,准备了一些毕业&答辩的材料,感觉离毕业越来越近了,加油学习喽~ 今日观看了石群老师…...

vue 文件预览mp4、txt、pptx、xls、xlsx、docx、pdf、html、xml
vue 文件预览 图片、mp4、txt、pptx、xls、xlsx、docx、pdf、html、xml 最近公司要做一个类似电脑文件夹的功能,支持文件夹操作,文件操作,这里就不说文件夹操作了,说说文件预览操作,本人是后端java开发,前端vue&#…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...