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