关闭

FUSE调用流程分析

162人阅读 评论(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

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:31662次
    • 积分:515
    • 等级:
    • 排名:千里之外
    • 原创:9篇
    • 转载:80篇
    • 译文:0篇
    • 评论:3条
    最新评论