免费注册 查看新帖 |

ChinaUnix.net

  平台 论坛 博客 文库 频道自动化运维 虚拟化 储存备份 C/C++ PHP MySQL 嵌入式 Linux系统
最近访问板块 发新帖
查看: 3805 | 回复: 0

fuse调用流程分析 [复制链接]

论坛徽章:
0
发表于 2010-02-03 03:02 |显示全部楼层

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
            break;
        fuse_session_process(se, buf, res, tmpch);   //处理读到的请求
    }

    free(buf);
    fuse_session_reset(se);
    return res
}

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



                                             



本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/87570/showart_2168457.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

数据风云,十年变迁
DTCC 第十届中国数据库技术大会已启航!

2019年5月8日~5月10日,由IT168旗下ITPUB企业社区平台主办的第十届中国数据库技术大会(DTCC2019),将在北京隆重召开。大会将邀请百余位行业专家,就热点技术话题进行分享,是广大数据领域从业人士的又一次年度盛会和交流平台。与SACC2018类似,本届大会将采用“3+2”模式:3天传统技术演讲+2天深度主题培训。大会不仅提供超100场的主题演讲,还会提供连续2天的深度课程培训,深化数据领域的项目落地实践方案。
DTCC2019,一场值得期待的数据技术盛会,殷切地希望您报名参与!

活动入口>>
  

北京盛拓优讯信息技术有限公司. 版权所有 16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122
中国互联网协会会员  联系我们:huangweiwei@it168.com
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP