FUSE 内核实现代码分析(二) 队列管理

fuse处理的请求和操作非常多,代码里有相当多的函数,分析起来很容易陷入函数调用泥潭里,所以要抓住关键分析。

fuse的关键是对请求队列的处理,所以搞清楚fuse如何管理请求队列是基础。先上一张关系图


这里一共有两个进程等待队列:一个是用户空间fuse库对/dev/fuse读写的进程等待队列,一个是发起VFS调用请求的进程的等待队列

一个请求pending队列:需要处理的请求都挂在这个队列上

下面再看相关数据结构

struct fuse_conn {
    /** Readers of the connection are waiting on this */
    wait_queue_head_t waitq;
    /** The list of pending requests */
    struct list_head pending;
    /** The list of requests being processed */
    struct list_head processing;
    /** Pending interrupts */
    struct list_head interrupts;

    ......
};

 struct fuse_req {
         /** This can be on either pending processing or io lists in fuse_conn */
         struct list_head list;

         /** Entry on the interrupts list  */
         struct list_head intr_entry;
        /** Used to wake up the task waiting for completion of request*/
        wait_queue_head_t waitq;
       ......
};

1. fuse守护进程在fuse_session_loop中一直read设备-->内核中fuse_dev_read-->fuse_dev_do_read-->request_wait

   将进程睡眠在fc->waitq上

/* Wait until a request is available on the pending list */
static void request_wait(struct fuse_conn *fc) 
__releases(fc->lock)
__acquires(fc->lock)
{
        DECLARE_WAITQUEUE(wait, current);

        add_wait_queue_exclusive(&fc->waitq, &wait);
        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);
        }    
        set_current_state(TASK_RUNNING);
        remove_wait_queue(&fc->waitq, &wait);
}

判断进程是否睡眠的条件是request_pending()

static int request_pending(struct fuse_conn *fc) 
{
        return !list_empty(&fc->pending) || !list_empty(&fc->interrupts) ||
                forget_pending(fc);
}

看三个请求队列是不是为空,为空则睡眠,不为空则处理

2. VFS文件系统操作请求使用fuse_request_send向fuse守护进程发送请求

static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
{
        BUG_ON(req->background);
        spin_lock(&fc->lock);
        if (!fc->connected)
                req->out.h.error = -ENOTCONN;
        else if (fc->conn_error)
                req->out.h.error = -ECONNREFUSED;
        else {
                req->in.h.unique = fuse_get_unique(fc);
                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);
}

void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
{
        req->isreply = 1; 
        __fuse_request_send(fc, req);
}
EXPORT_SYMBOL_GPL(fuse_request_send);

首先调用queue_request将请求放入fc->pending队列并唤醒用户空间fuse守护进程处理请求,

static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
{
        req->in.h.len = sizeof(struct fuse_in_header) +
                len_args(req->in.numargs, (struct fuse_arg *) req->in.args);
        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);
}

然后调用rquest_wait_answer睡眠在req->waitq上等待处理完成

static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
__releases(fc->lock)
__acquires(fc->lock)
{
        ... ...
         /*
         * Either request is already in userspace, or it was forced.
         * Wait it out.
         */
        spin_unlock(&fc->lock);
        wait_event(req->waitq, req->state == FUSE_REQ_FINISHED);
        spin_lock(&fc->lock);
        ... ...
}

3. fuse守护进程被唤醒,处理完成后,将结果写回fuse设备,并唤醒req->waitq上等待进程来取回结果,fuse_dev_write-->fuse_dev_do_write

static ssize_t fuse_dev_write(struct kiocb *iocb, const struct iovec *iov,
                              unsigned long nr_segs, loff_t pos)
{
        struct fuse_copy_state cs;
        struct fuse_conn *fc = fuse_get_conn(iocb->ki_filp);
        if (!fc)
                return -EPERM;

        fuse_copy_init(&cs, fc, 0, iov, nr_segs);

        return fuse_dev_do_write(fc, &cs, iov_length(iov, nr_segs));
}

/*
 * Write a single reply to a request.  First the header is copied from
 * the write buffer.  The request is then searched on the processing
 * list by the unique ID found in the header.  If found, then remove
 * it from the list and copy the rest of the buffer to the request.
 * The request is finished by calling request_end()
 */
static ssize_t fuse_dev_do_write(struct fuse_conn *fc,
                                 struct fuse_copy_state *cs, size_t nbytes)
{
        ... ...
        req = request_find(fc, oh.unique);
        ... ...
        request_end(fc, req);
        ... ...
}

4. VFS文件系统操作请求被唤醒,取回处理结果

/*
 * This function is called when a request is finished.  Either a reply
 * has arrived or it was aborted (and not yet sent) or some error
 * occurred during communication with userspace, or the device file
 * was closed.  The requester thread is woken up (if still waiting),
 * the 'end' callback is called if given, else the reference to the
 * request is released
 *
 * Called with fc->lock, unlocks it
 */
static void request_end(struct fuse_conn *fc, struct fuse_req *req)
__releases(fc->lock)
{
        ... ...
        req->state = FUSE_REQ_FINISHED;
        wake_up(&req->waitq);
        ... ...
}

下面以一个删除文件操作展示一下具体流程:>为调用,<为返回


     
到此可以看清fuse和用户空间交互的过程,fuse用户态库和内核代码对请求处理的细节待后续分析。


  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值