关闭

FUSE调用流程分析

340人阅读 评论(0) 收藏 举报

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等待返回结果。

 

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

}  

0
0
查看评论

[Ceph分析]Fuse流程分析

本文是对FUSE-2.9.2源码的学习总结。FUSE代码在用户空间和内核空间都有运行,为了突出重点,先简要描述了在基于FUSE的用户空间文件系统中执行write操作的一般流程
  • iamonlyme
  • iamonlyme
  • 2017-06-26 19:02
  • 714

FUSE调用流程分析

fuse处理请求的整个流程如下图所示,以unlink操作为例进行说明。其中“>”表示调用,”     fuse通过fuse_session_loop(或对应多线程的方法)来启动fuse守护程序,守护程序不断的从/dev/fuse上读取请求,并处理。 ...
  • Fybon
  • Fybon
  • 2014-03-12 16:00
  • 1406

android sdcard存储方案一(基于fuse文件系统)

 一、 启动三个相关service 转载地址:http://blog.csdn.net/fybon/article/details/25904215 按启动顺序,如下: service vold /system/bin/vold     class co...
  • kc58236582
  • kc58236582
  • 2015-12-31 17:17
  • 4862

吴锦华/明鑫: 用户态文件系统(FUSE)框架分析和实战

作者简介: 吴锦华,2015年毕业于西安电子科技大学,目前就职于诺基亚上海贝尔,从事嵌入式平台开发工作2年,负责对第三方boot和Linux移植和适配到公司的软件平台架构。 明鑫,2006年毕业于武汉大学,目前就职于诺基亚上海贝尔,从事嵌入式平台开发工作11年,先后参与了VxWorks、In...
  • juS3Ve
  • juS3Ve
  • 2017-10-14 00:00
  • 212

fuse调用流程有待整理

fuse处理请求的整个流程如下图所示,以unlink操作为例进行说明。其中“>”表示调用,”     fuse通过fuse_session_loop(或对应多线程的方法)来启动fuse守护程序,守护程序不断的从/dev/fuse上读取请求,并处理。 ...
  • S1234567_89
  • S1234567_89
  • 2012-08-14 16:40
  • 800

ceph存储 FUSE调用流程代码分析

fuse通过fuse_session_loop(或对应多线程的方法)来启动fuse守护程序,守护程序不断的从/dev/fuse上读取请求,并处理。   代码片段1 int fuse_session_loop(struct fuse_session *se) //在fuse_main...
  • skdkjxy
  • skdkjxy
  • 2015-04-02 15:00
  • 893

ceph存储 FUSE原理总结

1       概述 Fuse是filesystem in user space,一个用户空间的文件系统框架,允许非特权用户建立功能完备的文件系统,而不需要重新编译内核。fuse模块仅仅提供内核模块的入口,而本身的主要实现代码位于用户空...
  • skdkjxy
  • skdkjxy
  • 2014-12-31 20:20
  • 1990

ceph存储 FUSE API 的两种使用方法

FUSE 虚拟文件系统集成到我们的应用程序时,有两种使用策略,一种是使用比较上层的API, 主循环我们只能调用 ret = fuse_main (fargc, fargv, &my_handler, NULL) 这个主循环的接口,   my_handler我们只取我们关心AP...
  • skdkjxy
  • skdkjxy
  • 2015-04-02 14:54
  • 1412

do_fork实现分析

进程复制的三个机制fork、vfork和clone最终都是调用do_fork来实现子进程的产生的,不同的产生方式通过传递给do_fork的不同参数来控制。其代码执行流程如下: 代码: if (unlikely(clone_flags & CLONE_STOPPED)) { ...
  • CToday
  • CToday
  • 2014-03-07 11:32
  • 1007

socket调用流程分析

0、概述: 在这里将讲述用户层的socket函数如何调用到内核的sys_socket函数,有什么错误啊,还请各位大虾指出,哈哈~ 分析: 1、 函数声明 根据其头文件我们可以找到相应的声明; extern intsocket (int __domain, in...
  • cgr0609
  • cgr0609
  • 2014-08-17 23:30
  • 193
    个人资料
    • 访问:68220次
    • 积分:906
    • 等级:
    • 排名:千里之外
    • 原创:10篇
    • 转载:83篇
    • 译文:0篇
    • 评论:4条
    最新评论