`

fuse调用流程分析

阅读更多
fuse处理请求的整个流程如下图所示,以unlink操作为例进行说明。其中“>”表示调用,”<”表示返回,[]表示调用中所做的工作。本人结合fuse的源代码,对整个流程进行了分析。









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



代码片段1

int fuse_session_loop(struct fuse_session *se) //在fuse_main中会被调用,或其多线程版本

{

    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

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 0x21000

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() + 0x1000;

    bufsize = bufsize < MIN_BUFSIZE ? MIN_BUFSIZE : bufsize;

    return fuse_chan_new(&op, fd, bufsize, NULL);

}



代码片段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

    ………

}



以上的分析对应了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守护程序读取该请求并进行处理,接下来介绍处理请求所作的工作。




代码片段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在之前的文章http://blog.chinaunix.net/u2/87570/showart_2166461.html中已经介绍过了,开发者实现了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,

    };



  …….

}





代码片段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的介绍http://blog.chinaunix.net/u2/87570/showart_2166461.html

    fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);

    }

}



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



代码片段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使用的队列的管理,在流程图中也做了简单的说明,下一篇文章将详细分析队列的管理。



代码片段7

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

{

         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);

                   /* acquire extra reference, since request is still needed

                      after request_end() */

                   __fuse_get_request(req);

        //等待结果

                   request_wait_answer(fc, req);

         }

         spin_unlock(&fc->lock);

}







  • 大小: 127.6 KB
分享到:
评论

相关推荐

    fuse相关流程.docx

    描述了fuse的框架,组成部分,包括(用户态和内核态),以write为例分析了整个用户态和内核态的流程

    fuse-2.7.4.tar.gz 解压即可使用

    FUSE(用户空间文件系统)作为类UNIX系统平台上可加载的内核模块,允许非特权用户创建功能完备的文件系统,而不需要重新编译内核。FUSE模块仅仅提供kernel模块的接入口,而本身的主要实现代码位于用户空间中。对于...

    Fuse设计选型详解(个人总结)

    本文仅针对Fuse(熔断器)选型,PPTC&CPTC及其他过流保护装置或电路不在其列。针对Fuse设计选型相关基础知识介绍及选型参考标准、选型方法介绍等内容。

    Fuse React Redux v7.2

    Fuse React是用React钩子编写的(React的新特性允许您在不编写类的情况下使用状态和其他React特性)。 Fuse React是一个完整的React管理模板,遵循谷歌的材料设计指南。 Fuse React管理模板使用Material UI V5作为...

    FUSE文件系统展示示例

    FUSE文件系统参考

    fuse-2.8.0.tar.gz

    fuse的最新版本,fuse是用户自己空间上的文件系统,

    fuse-2.8.4.tar

    fuse-2.8.4

    Fuse ESB 4.3.1使用笔记

    Fuse ESB 4.3.1 个人使用 笔记

    fuse-2.9.3.tar.gz

    fuse-2.9.3.tar.gz

    fuse文件系统源码

    这个是fuse文件系统的源码,网上找起来麻烦!所以从内核中打包然后提供下载

    FUSE选型计算方法与参数计算案例

    针对Fuse选型相关的参数特性,讲解参数选型的计算过程,选型注意事项。定义Fuse选型的规范方法。定义Fuse选型的规范方法。

    fuse-2.8.6.tar.gz

    fuse-2.8.6.tar.gz

    fuse-2.9.2

    fuse source code. version is 2.9.2

    FUSE14.2-Angular 11 Material设计风格后台模板

    Fuse Angular 11/React双版本后台模版,具有开箱即用的AoT编译器支持以及完整的NgRx示例应用程序。 内置应用 日历应用 电子商务应用 邮件应用 邮件(NgRx)应用 聊天应用 文件管理器应用 联系人应用 待办事项 Scrum...

    fuse-3.1.1.tar.gz

    安装脚本:./configure;make; make install 挂载:modprobe fuse 验证:lsmod|grep fuse

    fuse-2.7.1.tar.gz

    Linux下挂载NTFS系统时候经常挂不上去,这时候就要用ntfs-3g工具,装这个工具之前要安装fuse,资源已给出。

    fuse 3g 挂载ntfs 方法

    centos 挂载ntfs所需要的步骤 我就是在挂载2TB的时候发现的 linux挂载NTFS分区移动硬盘2010-09-23 16:35CentOS 挂载NTFS分区移动硬盘 1.uname -r 查看当前的linux内核版本. [root@localhost Desktop]# uname -r ...

    fuse规格及选择与使用

    详细介绍了关于fuse的参数规格及使用,希望对大家家有用......

    Linux-fuse-2.7.4.版本

    fuse-2.7.4.tar版本,解压缩,即可编译安装。

    fuse 下载linux服务器挂载硬盘

    linux 下挂载硬盘 fuse 自己找的

Global site tag (gtag.js) - Google Analytics