用户态文件系统fuse学习【转】
本文转载自:https://blog.csdn.net/ty_laurel/article/details/51685193
FUSE概述
FUSE(用户态文件系统)是一个实现在用户空间的文件系统框架,通过FUSE内核模块的支持,使用者只需要根据fuse提供的接口实现具体的文件操作就可以实现一个文件系统。
在fuse出现以前,Linux中的文件系统都是完全实现在内核态,编写一个特定功能的文件系统,不管是代码编写还是调试都不太方便,就算是仅仅在现有传统文件系统上添加一个小小的功能,因为是在内核中实现仍需要做很大的工作量。在用户态文件系统FUSE出现后(2.6内核以后都支持fuse),就会大大的减少工作量,也会很方便的进行调试。编写FUSE文件系统时,只需要内核加载了fuse内核模块即可,不需要重新编译内核。
FUSE 特点
- 用户空间文件系统——类Unix OS的框架
- 允许非超户在用户空间开发文件系统
- 内核的API接口,使用fs-type操作
- 支持多种编程语言( c、c++、perl、java 等)
- 普通用户也可以挂载FUSE
- 不用重新编译内核
FUSE组成
fuse主要由三部分组成:FUSE内核模块、用户空间库libfuse以及挂载工具fusermount。
- fuse内核模块:实现了和VFS的对接,实现了一个能被用户空间进程打开的设备,当VFS发来文件操作请求之后,将请求转化为特定格式,并通过设备传递给用户空间进程,用户空间进程在处理完请求后,将结果返回给fuse内核模块,内核模块再将其还原为Linux kernel需要的格式,并返回给VFS;
- fuse库libfuse:负责和内核空间通信,接收来自/dev/fuse的请求,并将其转化为一系列的函数调用,将结果写回到/dev/fuse;提供的函数可以对fuse文件系统进行挂载卸载、从linux内核读取请求以及发送响应到内核。libfuse提供了两个APIs:一个“high-level”同步API 和一个“low-level” 异步API 。这两种API 都从内核接收请求传递到主程序(fuse_main函数),主程序使用相应的回调函数进行处理。当使用high-level API时,回调函数使用文件名(file names)和路径(paths)工作,而不是索引节点inodes,回调函数返回时也就是一个请求处理的完成。使用low-level API 时,回调函数必须使用索引节点inode工作,响应发送必须显示的使用一套单独的API函数。
- 挂载工具:实现对用户态文件系统的挂载
FUSE主要代码文件
in kernel:
- kernel/inode.c —> 主要完成fuse文件驱动模块的注册,提供对supper block的维护函数以及其它(驱动的组织开始文件)
- kernel/dev.c —> fuse 的(虚拟)设备驱动
- kernel/control.c —> 提供对于dentry的维护及其它
- kernel/dir.c —> 主要提供对于目录inode索引节点的维护
- kernel/file.c —> 主要提供对于文件inode索引节点的维护
in userspace:
- lib/helper.c —> “fuse_main()”调用的主入口
- lib/fuse_kern_chan.c—>主要实现fuse应用层访问(读写)fuse driver的功能
- lib/fuse_mt.c —> fuse 的mount管理
- lib/fuse.c —> lib库主框架文件,实现了主要框架及对”用户实现的文件系统操作代码”的封装
- lib/fuse_lowlevel.c –> 实现比较底层的函数封装,供fuse.c等使用
- lib/fuse_loop.c —> fuse lib循环监视”fuse driver”的通信缓存
- lib/fuse_loop_mt.c —> 同fuse_loop.c
- lib/fuse_session.c —> fuse会话管理
Fuse是怎么工作的(fuse-2.9)?
1. fuse库
- 1.在用户态程序调用fuse_main() (lib/helper.c)时,先调用fuse_setup_common()该函数先解析用户态程序传递过来的参数,然后调用fuse_mount_common()(该函数是fuse_kern_mount()函数的封装,lib/mount.c)。
fuse_main()是一个宏定义(include/fuse.h),如下:
#define fuse_main(argc, argv, op, user_data) \
fuse_main_real(argc, argv, op, sizeof(*(op)), user_data)
- 2.fuse_kern_mount()函数中调用fuse_mount_fusermount()使用socketpair()创建一个UNIX域套接字,然后使用创建子进程执行fusermount程序,将FUSE_COMMFD_ENV环境变量中套接字的一端传递给它。
- 3.fusermount(util/fusermount.c)确保fuse 模块已经被加载,然后打开/dev/fuse并通过一个UNIX套接字发送文件处理句柄。父进程等待子进程执行完毕回收,然后返回fuse_mount_fusermount()函数。
- 4.fuse_kern_mount()通过/dev/fuse返回文件句柄给fuse_kern_chan_new()负责处理内核数据,然后返回到fuse_mount_common()函数。
- 5.fuse_setup_common()函数调用fuse_new_common(lib/fuse.c),fuse_new_common()函数分配fuse数据结构,存储并维护一个文件系统数据镜像缓存cached,返回到fuse_main()。
- 6.最后,fuse_main()调用fuse_loop(lib/fuse.c)或者fuse_loop_mt()(lib/fuse_mt.c),这两个函数都可以从设备/dev/fuse读取文件系统调用,调用fuse_main()之前调用存储在fuse_operations结构体中的用户态函数。这些调用的结果回写到/dev/fuse设备(这个设备可以转发给系统调用)。
2.内核模块
内核模块由2个部分组成:
第一个是proc文件系统组件(在kernel/dev.c中);
第二个是文件系统调用(kernel/file.c、kernel/inode.c、kernel/dir.c)。
kernel/file.c、kernel/inode.c、kernel/dir.c中的所有系统调用要么调用request_send(),要么调用request_send_noreply()或者request_send_nonblock()。大部分都是调用request_send()函数,它添加请求到“list of request”结构体(fc->pending),然后等待一个响应。request_send_noreply()和request_send_nonblock()与request_send()函数相似,除了是非阻塞的和不响应一个回复。
kernel/dev.c中的proc文件系统组件响应文件IO请求,fuse_dev_read()处理文件读,并从请求列表结构体(list of requests)返回命令到调用程序。fuse_dev_write()处理文件写, 完成数据写并放入req->out结构体(它能返回系统调用通过请求列表结构体和request_send()函数),
用户进程和操作系统进行交互(read文件为例):
该fuse文件系统挂载在现有ext4文件系统之上.
1.一个用户进程发出read文件请求;
2.该请求被转换为一个内核系统调用,内核VFS层调用fuse文件系统内核模块;
3.fuse 内核模块通过/dev/fuse,将read请求传递到fuse 用户态进程;
4.fuse daemon根据用户实现的read接口,产生新的系统调用,最终调用ext4文件系统的read操作函数,从存储介质中提取读操作要求的数据(page cache中有,直接从其中获取,否则读磁盘);
5.内核将数据返回给fuse文件系统;
6.用户级文件系统再次调用内核操作,把数据返回给用户进程;
7.内核将数据传给用户进程完成操作。
库函数fuse_main()具体处理流程:
1.先打开设备文件/dev/fuse;
2.然后挂载FUSE文件系统;
3.产生FUSE文件系统指针;
4.初始化FUSE文件系统的操作函数集:
5.初始化信号处理函数集;
6.进入等待循环:
从设备文件/dev/fuse中读取来自内核模块的请求;
运行相应的操作函数,并获取返回结果;
将返回给内核的应答结果写入设备文件/dev/fuse中;
注意
fuse本质上(数据处理时)是处于现有文件系统之上的(具体实现是和现有文件系统处于同一个层次的),fuse不参与底层磁盘数据的存取,只负责处理对读取和写入的数据在逻辑上的操作而已。
安装使用fuse
软件包下载https://github.com/libfuse/libfuse/releases:
下载软件包,解压后,编译安装。
./configure
make
make install
解压后的目录中有名为example的目录,其中有fuse自带的几种fuse用户态实现例子,可以运行其进行测试。
fuse安装完后,该目录中的文件也已经编译成功,只需运行即可(目录dir为挂载点,挂载成功后,在该目录中对文件的操作就会调用fuse自己实现的操作函数):
//example/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); //主函数,会调用文件操作结构体
}
ty@ubuntu:~/program/fuse/fuse-2.9.6/example/dir$ mkdir dir //创建挂载点
ty@ubuntu:~/program/fuse/fuse-2.9.6/example/dir$ ./hello dir //将hello fuse文件系统挂载在dir目录上
根据目前实现的四个功能进行测试如下:
ty@ubuntu:~/program/fuse/fuse-2.9.6/example/dir$ ls
hello
ty@ubuntu:~/program/fuse/fuse-2.9.6/example/dir$ cat hello
Hello World!
ty@ubuntu:~/program/fuse/fuse-2.9.6/example/dir$ ls -l hello
-r--r--r-- 1 root root 13 12月 31 1969 hello
ty@ubuntu:~/program/fuse/fuse-2.9.6/example/dir$ rm -fr hello
rm: cannot remove ‘hello’: Function not implemented
ty@ubuntu:~/program/fuse/fuse-2.9.6/example/dir$ touch test
touch: cannot touch ‘test’: Function not implemented
ty@ubuntu:~/program/fuse/fuse-2.9.6/example/dir$ mkdir test
mkdir: cannot create directory ‘test’: Function not implemented
由测试可以发现在dir目录中只能对文件进行实现的四个功能的操作,其他的操作都无法完成,会提示用户函数没有实现。 ty@ubuntu:~/program/fuse/fuse-2.9.6/example/$ dirfusermount -u dir //卸载fuse文件系统dir
用户态文件系统fuse学习【转】的更多相关文章
- BlueStore-先进的用户态文件系统《二》-BlueFS
https://zhuanlan.zhihu.com/p/46362124 简介 上一篇文章中,介绍了BlueStore的诞生背景.逻辑架构以及设计思想,提到了在BlueStore中元数据都是存放在R ...
- BlueStore-先进的用户态文件系统《一》
https://zhuanlan.zhihu.com/p/45084771 分布式存储系统通过将数据分散到多台机器上来充分利用多台机器的资源提高系统的存储能力,每台机器上的数据存放都需要本地的单机存储 ...
- FUSE 用户空间文件系统 (Filesystem in Userspace)
FUSE 仓库 Wiki FUSE 性能评测 关于Fuse文件系统: FUSE (Filesystem in Userspace) is an interface for userspace prog ...
- 2.2.1 用户态、内核态的形成 -《zobolの操作系统学习札记》
内核态的出现,让计算机系统的权力向操作系统高度集中了. 操作系统分出内核态和用户态,就是为了进行不同等级的权限管理, 从而更好的适应多用户多任务并发的工作环境. 用户态和内核态的来源 在早期的单进程单 ...
- Linux操作系统学习_用户态与内核态之切换过程
因为操作系统的很多操作会消耗系统的物理资源,例如创建一个新进程时,要做很多底层的细致工作,如分配物理内存,从父进程拷贝相关信息,拷贝设置页目录.页表等,这些操作显然不能随便让任何程序都可以做,于是就产 ...
- go语言学习--内核态和用户态(协程)
go中的一个特点就是引入了相比于线程更加轻量级的协程(用户态的线程),那么什么是用户态和内核态呢? 一.什么是用户态和内核态 当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核 ...
- [中英对照]User-Space Device Drivers in Linux: A First Look | 初识Linux用户态设备驱动程序
如对Linux用户态驱动程序开发有兴趣,请阅读本文,否则请飘过. User-Space Device Drivers in Linux: A First Look | 初识Linux用户态设备驱动程序 ...
- C1000k 新思路:用户态 TCP/IP 协议栈
现在的服务器支撑上百万个并发 TCP 连接已经不是新闻(余锋2010年的演讲,ideawu 的 iComet 开源项目,WhatsApp 做到了 2.5M).实现 C1000k 的常规做法是调整内核参 ...
- Linux探秘之用户态与内核态
一. Unix/Linux的体系架构 如上图所示,从宏观上来看,Linux操作系统的体系架构分为用户态和内核态(或者用户空间和内核).内核从本质上看是一种软件——控制计算机的硬件资源,并提供上层应用程 ...
随机推荐
- SpringMVC Controller 介绍【转】
链接地址: http://haohaoxuexi.iteye.com/blog/1753271
- Codis的了解和操作
1.Codis的基本架构 2.Codis各组件 Codis-server:就是redis服务,可以使用codis修改的reids和原生的redis Codis-proxy:客户端连接的代理服务,客户端 ...
- 【唯星宠物】——BootStrap/Mysql/PHP/Ajax爬坑之正则验证登录注册子页
前言:唯星宠物产品官网的登录注册,单独一个子页,页面使用BootStrap实现响应式,PHP提供服务端,利用Ajax技术,从Mysql中获取JSON数据,并对表单信息进行正则验证.项目github地址 ...
- 转: https 加密通信流程
https 加密通信流程当用户在浏览器中输入一个以https开头的网址时,便开启了浏览器与被访问站点之间的加密通信.下面我们以一个用户访问https://qbox.me为例,给读者展现一下SSL/TL ...
- .NET Core R2安装及示例教程
.NET Core R2安装及示例教程 Install for Windows - Visual Studio 2015 1 Download Visual Studio 2015 Make sure ...
- 13.2 处理静态资源【从零开始学Spring Boot】
转载:http://blog.csdn.net/linxingliang/article/details/51637052目录(?)[-] 默认资源映射 自定义资源映射 自定义目录 使用外部目录 通过 ...
- js 查找一串字符串中一段字符
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- NYOJ92 图像实用区域 【BFS】
碰到了一个曾经从未见过的奇怪问题:先上截图: 执行号 用户 题目 结果 时间 内存 语言 提交时间 895360 userid=%E9%95%BF%E6%9C%A8" style=" ...
- c语言字符数组的初始化问题
1.字符数组的定义与初始化 字符数组的初始化,最容易理解的方式就是逐个字符赋给数组中各元素. char str[10]={ 'I',' ','a','m',' ',‘h’,'a','p','p','y ...
- springMVC --@RequestParam注解(后台控制器获取參数)
在SpringMVC后台控制层获取參数的方式主要有两种,一种是request.getParameter("name"),第二种是用注解@RequestParam直接获取. 1.获取 ...