Fuse是filesystem in user space,一个用户空间的文件系统框架,允许非特权用户建立功能完备的文件系统,而不需要重新编译内核。fuse模块仅仅提供内核模块的入口,而本身的主要实现代码位于用户空间中。对于读写虚拟文件系统来讲,fuse是个很好的选择。fuse包含包含一个内核模块和一个用户空间守护进程,将大部分的VFS调用都委托一个专用的守护进程来处理。

2

工作原理

Fuse用户空间文件系统与真实的文件系统不同,它的supper block, indoe, dentry等都是由内存虚拟而来,具体在物理磁盘上存储的真实文件结构是什么,它不关心,且对真实数据的请求通过驱动和接口一层层传递到用户空间的用户编写的具体实现程序里来,这样就为用户开发自己的文件系统提供了便利,这也就是所谓的“用户空间文件系统”的基本工作理念。

2.1

模块架构

FUSE分为三大模块:

Ø FUSE内核模块(内核态)

Ø LibFUSE模块(用户态)

Ø 用户程序模块(用户态)

用户程序在用户空间实现LibFUSE库封装的文件系统操作;

LibFUSE实现文件系统主要框架、对“用户实现的文件系统操作代码“的封装、mount管理、通过字符设备/dev/fuse与内核模块通信;

FUSE内核模块实现VFS接口(实现fuse文件驱动模块的注册、fuse

的(虚拟)设备驱动、提供supper block、dentry、inode的维护),接收来至后者的请求,传递给LibFUSE,LibFUSE再传递给我们用户程序的接口进行实现操作。

图一

2.1.1

源代码结构及开发库

FUSE内核模块

kernel/inode.c —> 主要完成fuse文件驱动模块的注册,提供对supper

block的维护函数以及其它(驱动的组织开始文件)

kernel/dev.c —> fuse 的(虚拟)设备驱动

kernel/control.c

—> 提供对于dentry的维护及其它

kernel/dir.c —> 主要提供对于目录inode索引节点的维护

kernel/file.c —> 主要提供对于文件inode索引节点的维护

LibFUSE模块

lib/helper.c —>

“fuse_main()”调用的主入口

lib/fuse_kern_chan.c—>主要实现fuse应用层访问(读写)fuse

driver的功能

lib/mount_util.c

–> 提供mount的基础函数调用

lib/mount.c —> 主要实现设备的“mount”、“umount”等挂接操作

lib/mount_bsd.c

—> “Free bsd”下的“mount”、“umount”实现

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

—> 同上

lib/fuse_session.c

—> fuse会话管理

开发库

include/fuse.h —> the library interface of FUSE (High

Level)

include/fuse_common.h —> common

include/fuse_lowlevel.h —> Lowlevel API

include/fuse_opt.h —> option parsing interface of FUSE

2.2

系统工作流程

fuse_main(lib/helper.c)–fuse用户空间主函数,用户程序调用它时,fuse_main函数解析相关函数(如mountpoint,multithreaded),并调用fuse_mount函数。调用fuse_new()函数,为fuse文件系统数据分配存储空间。调用fuse_loop()函数实现会话的接受与处理。

fuse_mount(lib/mount.c)–创建UNIX本地套接口,创建并运行子进程fusermount.并返回fuse模块文件fd给fuse_main()函数。

fusemount(util/fusermount.c)–确保fuse模块已经加载,通过UNIX套接口返回fuse模块的文件fd给fuse_mount()函数。

fusermount提供一系列挂载选项,如direct_io(跳过页缓存),allow_root(允许root访问挂载的文件系统),allow_other(允许其他用户访问挂载的文件系统),nonempty(允许把文件系统挂载到非空目录),big_writes(支持大于4k的写操作)等

fuse_new(lib/fuse.c)–为fuse创建数据结构空间,用来存储文件系统数据。

fuse_loop(lib/fuse.c)(fuse_loop_mt(lib/fuse_mt.c))–从/dev/fuse读取文件系统调用,调用fuse_operation结构中的处理函数,返回调用结果给/dev/fuse.

如图二:

图二

fuse处理请求的整个流程如图三所示,以unlink操作为例进行说明。其中“>”表示调用,”

图三

fuse通过fuse_session_loop(或对应多线程的方法)来启动fuse守护程序,守护程序不断的从/dev/fuse上读取请求,并处理。

2.3.1

重要数据结构

l struct fuse_req(fuse_i.h)

—> 发往客户端的请求

struct fuse_req

{

struct fuse_ll *f;

uint64_t unique;

int ctr;

pthread_mutex_t lock;

struct fuse_ctx ctx;

struct fuse_chan *ch;

int interrupted;

union {

struct {

uint64_t unique;

} i;

struct {

fuse_interrupt_func_t func;

void *data;

} ni;

} u;

struct fuse_req *next;

struct fuse_req *prev;

};

l struct fuse_session(fuse_session.c)

—> 客户端管理会话的结构体,其包含“struct

fuse_chan”结构

struct fuse_session

{

struct fuse_session_ops op;

void *data;

volatile int exited;

struct fuse_chan *ch;

};

l struct fuse_chan(fuse_session.c)

—> 定义客户端与fuse内核连接隧道的结构体

struct fuse_chan

{

struct fuse_chan_ops op;

struct fuse_session *se;

int fd;

size_t bufsize;

void *data;

int compat;

};

说明:这里的连接隧道不是什么具体的网络连接,而是客户端通过fuse设备驱

动来读写设备缓存(以与设备交互的一条概念上的隧道

l fc数据结构:

struct

fuse_conn

{

……….

wait_queue_head_t

waitq;

struct list_head

pending;

struct list_head

processing;

……….

};

2.3.2

代码片段1

//在fuse_main中会被调用,或其多线程版本

int

fuse_session_loop(struct fuse_session

*se)

{

int res = 0;

struct fuse_chan *ch = fuse_session_next_chan(se, NULL);

size_t bufsize = fuse_chan_bufsize(ch);

char *buf = (char *) malloc(bufsize); //为channel分配好缓冲区

if (!buf) {

fprintf(stderr, “fuse: failed to allocate read

buffer\n”);

return -1;

}

//fuse daemon, loops

while (!fuse_session_exited(se)) {

struct fuse_chan *tmpch = ch;

//分析见代码片段2,从/dev/fuse读请求,会等待一直到有请求为止

res = fuse_chan_recv(&tmpch,

buf, bufsize);

if (res == -EINTR)

continue;

if (res <= 0)

break;

fuse_session_process(se, buf, res,

tmpch); //处理读到的请求

}

free(buf);

fuse_session_reset(se);

return res < 0 ? -1 : 0;

}

2.3.3

代码片段2

int

fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t

size)

{

struct fuse_chan *ch = *chp;

if (ch->compat)

return ((struct fuse_chan_ops_compat24 *)

&ch->op)

->receive(ch, buf, size);

else

return ch->op.receive(chp,

buf, size);

//由下面的一段代码可以发现,receive最终是通过fuse_kern_chan_receive实现的,代码片段3分析该请求

}

#define MIN_BUFSIZE

0×21000

struct fuse_chan

*fuse_kern_chan_new(int fd)

{

//channel的读写方法

struct fuse_chan_ops op =

{

.receive = fuse_kern_chan_receive,

.send = fuse_kern_chan_send,

.destroy = fuse_kern_chan_destroy,

};

//设置bufsize大小

size_t bufsize = getpagesize() + 0×1000;

bufsize = bufsize < MIN_BUFSIZE ? MIN_BUFSIZE :

bufsize;

return fuse_chan_new(&op, fd, bufsize,

NULL);

}

2.3.4

代码片段3

static int

fuse_kern_chan_receive(struct fuse_chan **chp, char

*buf,

size_t size)

{

struct fuse_chan *ch = *chp;

int err;

ssize_t res;

struct fuse_session *se = fuse_chan_session(ch);

assert(se != NULL);

// 一直轮询,直到读到请求为止

restart:

//fuse_chan_fs获取到/dev/fuse的文件描述符,调用read系统调用从设备读取请求

res =

read(fuse_chan_fd(ch), buf, size);

//根据fuse设备驱动程序file结构的实现(dev.c),read将调用fuse_dev_read,该方法最终通过fuse_dev_readv实现,根据代码中的注释,fuse_dev_read做了如下工作:

Read a single request into the userspace

filesystem’s buffer. This

function waits until a request is available, then

removes it from

the

pending list and copies request data to userspace

buffer.

if no data: goto restart

………

}

2.3.5

一阶段小结

以上的分析对应了fuse filesystem daemon做的第一部分工作。当用户从控制台输入“rm

/mnt/fuse/file”时,通过VFS(sys_unlink),再到fuse(dir.c中实现的inode_operations,file.c中实现的file_operations中的方法都会最终调用request_send,后面会介绍),这个请求最终被发到了/dev/fuse中,该请求的到达会唤醒正在等待的fuse守护程序,fuse守护程序读取该请求并进行处理,接下来介绍处理请求所作的工作。

2.3.6

代码片段4

struct fuse_session

*fuse_lowlevel_new_common(struct fuse_args *args,

const struct fuse_lowlevel_ops

*op,

size_t op_size, void *userdata)

{

//开发者实现了fuse_lowlevel_ops并传递给fuse_lowlevel_common

struct fuse_ll *f;

struct fuse_session *se;

struct

fuse_session_ops sop = {

//最终调用的处理方法

.process =

fuse_ll_process, //分析见代码片段5

.destroy = fuse_ll_destroy,

};

…….

}

2.3.7

代码片段5

static void

fuse_ll_process(void *data, const char *buf, size_t len,

struct fuse_chan *ch)

{

struct fuse_ll *f = (struct fuse_ll *) data;

struct fuse_in_header *in = (struct fuse_in_header *)

buf;

const void *inarg = buf + sizeof(struct fuse_in_header);

struct fuse_req

*req;

//创建并初始化一个请求

req = (struct fuse_req *) calloc(1, sizeof(struct

fuse_req));

if (req == NULL) {

fprintf(stderr, “fuse: failed to allocate request\n”);

return;

}

req->f = f;

req->unique =

in->unique;

……

//根据opcode调用fuse_ll_ops中相应的方法

fuse_ll_ops[in->opcode].func(req,

in->nodeid, inarg);

}

}

2.3.8

二阶段小结

以上代码对应中流程中perform unlink的工作,实际上就是执行开发者实现的一组方法来完成相关的工作,接下来就是把执行完请求后需要的数据返回,最终是通过send_reply实现的,

2.3.9

代码片段6

static int

send_reply(fuse_req_t req, int error, const void *arg,

size_t argsize)

{

struct iovec iov[2];

int count = 1;

if (argsize) {

iov[1].iov_base = (void *) arg;

iov[1].iov_len = argsize;

count++;

}

return send_reply_iov(req, error, iov,

count);

}

static int

send_reply_iov(fuse_req_t req, int error, struct iovec

*iov,

int count)

{

……

res = fuse_chan_send(req->ch, iov,

count);

free_req(req);

return res;

}

static int

fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec

iov[],

size_t count)

{

if (iov) {

//将数据写到/dev/fuse上,最终会调用fuse_dev_write

ssize_t res = writev(fuse_chan_fd(ch), iov,

count);

……

return 0;

}

另外fuse接收到VFS的请求时,通过request_send将请求发送到/fuse/dev,并调用request_wait_answer等待返回结果。至于fuse使用的队列的管理,在流程图中也做了简单的说明,下一章将详细分析队列的管理。

2.3.10

代码片段7

{

req->isreply = 1;

spin_lock(&fc->lock);

if (!fc->connected)

req->out.h.error = -ENOTCONN;

else if (fc->conn_error)

req->out.h.error = -ECONNREFUSED;

else {

//将请求加入请求队列

queue_request(fc, req);

__fuse_get_request(req);

//等待结果

request_wait_answer(fc,

req);

}

spin_unlock(&fc->lock);

}

2.4

队列管理

fuse通过fuse_session_loop来启动守护程序,守护程序最终会调用fuse_dev_readv,fuse_dev_readv调用request_wait,使得进程在fc的waitq队列上睡眠。

2.4.1

代码片段1

static ssize_t

fuse_dev_readv(struct file *file, const struct iovec

*iov,

unsigned long nr_segs, loff_t *off)

{

…..

request_wait(fc);

….

}

static void

request_wait(struct fuse_conn *fc)

{

//定义一个队列节点变量wait,其与当前进程相关联

DECLARE_WAITQUEUE(wait, current);

//将wait加入到fc->waitq等待队列中,当有请求发到fuse文件系统时(通过request_send),这个等待队列上的进程会被唤醒,某一个进程会被赋予CPU使用权

add_wait_queue_exclusive(&fc->waitq,

&wait);

//不断的检查fc的pending队列及interrupts队列,看是否有请求,没有请求会一直while循环

while (fc->connected

&& !request_pending(fc)) {

set_current_state(TASK_INTERRUPTIBLE);

if (signal_pending(current))

break;

spin_unlock(&fc->lock);

schedule(); //选择一个进程运行

spin_lock(&fc->lock);

}

//有请求,将进程设为TASK_RUNNING状态

set_current_state(TASK_RUNNING);

//将wait从等待队列中移除

remove_wait_queue(&fc->waitq,

&wait);

}

static int

request_pending(struct fuse_conn *fc)

{

return !list_empty(&fc->pending) ||

!list_empty(&fc->interrupts);

}

2.4.2

代码片段2

request_send是用户请求经过vfs,再到fuse operation中被调用的,它向/dev/fuse发送请求

void

request_send(struct fuse_conn *fc, struct fuse_req *req)

{

……

queue_request(fc,

req);

request_wait_answer(fc,

req);

……

}

{

//将请求加入到pending队列

list_add_tail(&req->list,

&fc->pending);

req->state = FUSE_REQ_PENDING;

if (!req->waiting) {

req->waiting = 1;

atomic_inc(&fc->num_waiting);

}

//唤醒等待等列

wake_up(&fc->waitq);

kill_fasync(&fc->fasync, SIGIO,

POLL_IN);

}

static void

request_wait_answer(struct fuse_conn *fc, struct fuse_req

*req)

{

//该调用会在req的waitq上睡眠,fuse守护程序处理完请求后,会将其唤醒

}

fuse守护程序处理完请求,最终通过fuse_dev_writev写回/dev/fuse,它将唤醒相应req中waitq的等待队列元素,从而让文件系统请求完成request_wait_answer,获取到结果。

static ssize_t

fuse_dev_writev(struct file *file, const struct iovec

*iov,

unsigned long nr_segs, loff_t *off)

{

req = request_find(fc,

oh.unique);

request_end(fc, req);

}

{

//唤醒req上的等待队列

wake_up(&req->waitq);

}

fuse设备其的主要工作其实就是进行队列的管理,对fuse设备的读(写)其实就是从相应的队列移除(添加)请求(或响应),request_send将请求加入pending队列,唤醒fuse守护程序,并在req的waitq上等待请求结果,守护程序通过fuse_dev_readv从pending队列中移除请求并处理,处理完成后,守护程序唤醒req的waitq上的进程,该进程读取结果,并返回给用户。总的来说,一个请求从发起到完成会经过4步:

ü fuse守护程序在fc的waitq上等待请求.

ü

fuse deamon process wait

ü 用户的请求唤醒fc的waitq,从该waitq上移除一个请求进行处理,并在req的waitq上等待请求结果;

ü fuse守护程序被唤醒,读取请求,处理请求,返回结果,唤醒对应req上的waitq队列。

ü 请求被唤醒,读取fuse守护程序返回的结果,返回给用户。

3

开发API

4

参考资料

linux fuse 阻塞,FUSE原理总结相关推荐

  1. java线程池_Java多线程并发:线程基本方法+线程池原理+阻塞队列原理技术分享...

    线程基本方法有哪些? 线程相关的基本方法有 wait,notify,notifyAll,sleep,join,yield 等. 线程等待(wait) 调用该方法的线程进入 WAITING 状态,只有等 ...

  2. Linux 文件系统的工作原理深度透析

    磁盘为系统提供了最基本的持久化存储. 文件系统则在磁盘的基础上,提供了一个用来管理文件的树状结构. 那么,磁盘和文件系统是怎么工作的呢?又有哪些指标可以衡量它们的性能呢? 索引节点和目录项 文件系统, ...

  3. linux 非阻塞 socket - Google 搜索

    linux 非阻塞 socket - Google 搜索 linux c实现超时.非阻塞socket的函数select - Yunlu Liu (刘云璐) sites.google.com/site/ ...

  4. Linux 原生异步 IO 原理与使用

    目录 什么是异步 IO? Linux 原生 AIO 原理 Linux 原生 AIO 使用 什么是异步 IO? 异步 IO:当应用程序发起一个 IO 操作后,调用者不能立刻得到结果,而是在内核完成 IO ...

  5. Linux 下 TC 命令原理及详解<一>

    文章目录 1 前言 2 相关概念 3 使用TC 4 创建HTB队列 5 为根队列创建相应的类别 6 为各个类别设置过滤器 7 复杂的实例 Linux 下 TC 命令原理及详解<一> Lin ...

  6. linux从接通电源到操作系统启动,第4章-Linux引导过程及原理要点.ppt

    <第4章-Linux引导过程及原理要点.ppt>由会员分享,可在线阅读,更多相关<第4章-Linux引导过程及原理要点.ppt(98页珍藏版)>请在人人文库网上搜索. 1.Li ...

  7. linux 随机数原理,Linux随机数生成器的原理和缺陷.pdf

    第17卷.第10期 计算机技术与发展 vol.17No.10 2007年10月 COMPUTERTECHNOLOGYANDDEVELOPMENT Oct.2007 Linux随机数生成器的原理及缺陷 ...

  8. linux随机数原理,Linux随机数生成器的原理与缺陷.pdf

    第17卷.第10期 计算机技术与发展 vol.17No.10 2007年10月 COMPUTERTECHNOLOGYANDDEVELOPMENT Oct.2007 Linux随机数生成器的原理及缺陷 ...

  9. Linux I/O底层原理揭秘

    本文转载腾讯游戏工程师:Linux I/O底层原理全面揭秘 这里向作者致敬,写的非常棒,从技术背景到原理,通俗易懂. 摘要 从虚拟内存.I/O 缓冲区,用户态&内核态以及 I/O 模式等等知识 ...

最新文章

  1. 机器学习、深度学习、自然语言处理、计算机视觉顶级期刊的论文资料分享(附顶会论文下载链接)...
  2. 希尔排序 - 数据结构和算法91
  3. 从零开始学python网络爬虫-从零开始学Python 三(网络爬虫)
  4. mongodb 3.4单实例安装与备
  5. CSS清浮动处理(Clear与BFC)
  6. 过滤请求绝技 — 布隆过滤器与布谷鸟过滤器
  7. java 如何将word 转换为ftl_使用 freemarker导出word文档
  8. Linux C获取文件属性
  9. Docker Compose配置springboot微服务项目
  10. go语言web编程,初学点滴记录1
  11. 使用vs2008搭建php扩展环境
  12. 线性代数-n维向量知识点总结
  13. 自然语言处理 | (30) 文本相似度计算与文本匹配问题
  14. 取消参考文献自动编号_取消参考文献引用 - 卡饭网
  15. 波束赋形beamforming
  16. kde下gwenview启动慢,甚至几十秒才能启动
  17. 推荐一个查询研究者方向和影响力的网站| 也可以查询杂志是否有专刊开放
  18. 微信支付 Verify the signature and get the Wechatpay certificate corresponding to serialnumber[X] is empt
  19. 华为机试python打印机_华为校园招聘上机笔试题 扑克牌大小(python)
  20. 2014中国飞思卡尔技术论坛即将开幕

热门文章

  1. 贾跃亭致信债权人:将努力打工还债,请相信我!
  2. 华强北万事俱备,只待 iPhone 11到货
  3. 锐龙5 3600偷跑 性能直逼酷睿i9-9900K
  4. 假的!微信上询问商品会被封号 微信朋友圈公布1月份十大谣言
  5. Linux下多线程编程
  6. Netty之线程模型
  7. flutter 的gradle下载不了怎么办
  8. MFC小笔记:开机自动启动
  9. gitbook使用实录
  10. onvif学习笔记4:Windows环境使用gsoap生成onvif框架代码