fuse用户态、内核态通信机制分析

来源:互联网 发布:php与mysql web开发五 编辑:程序博客网 时间:2024/05/02 00:25
关于fuse用户态文件系统的文章有很多,比如http://my.debugman.net/program/fuse-180.html,就写得很全面。但关于fuse用户态、内核态通信的文章还比较少,我现在发现的一篇是http://blog.chinaunix.net/uid-20687780-id-313603.html,主要讲解了用户态、内核态的通信协议。

这里主要分析一下fuse的内核态用户态通信机制。fuse的主要运行流程如下图所示:

当用户态程序执行了POSIX的文件系统操作,经过glibc,变换为系统调用传递给vfs,vfs再将其传给FUSE的内核模块,FUSE的内核模块根据系统调用的类型,将请求发送到用户态的FUSE进程,并等待用户态进程的应答。FUSE内核模块再收到应答后,将其发送给vfs,把最终运行结果呈现到用户态程序。

那FUSE是如何让用户态与内核态通信的呢?这个在源代码中可以看得比较清楚。

首先在,内核代码fs/fuse/dev.c中,

通过调用fuse_dev_init函数,将会生成一个misc设备(类似字符设备,但主设备号为10,并且会在/dev/目录下根据设备名,自动生成设备文件)在/dev/fuse下。用户态代码在通过open这个设备文件,并且通过如下函数,注册向fuse内核态通信的函数:
fuse_kern_chan_receive函数,通过res = read(fuse_chan_fd(ch), buf, size);从/dev/fuse中读取内核发来的情求,再通过fuse_kern_chan_send函数中的ssize_t res = writev(fuse_chan_fd(ch), iov, count);将数据发送到内核模块。

再回到内核模块,还是fs/fuse/dev.c文件中,FUSE通过为/dev/fuse设备文件注册以下操作回调来支持用户态的对其的读写操作:

其中,do_sync_read中,调用了ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos),同样do_sync_write函数中,也调用了ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos),所以他们不用单独实现。

在FUSE内核中,存在一个fuse_conn结构体,为用户态、内核态通信服务,其结构为:


fuse_conn结构体的指针将会保存在file->private_data中,每次内核态向用户态发送情求时都会用到fuse_conn结构体。在fuse_dev_read函数的处理流程主要入下:

static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov,      unsigned long nr_segs, loff_t pos){//省略变量定义struct fuse_conn *fc = fuse_get_conn(file);   /* 获得fuse_conn结构体的指针 */if (!fc)return -EPERM; restart:spin_lock(&fc->lock);err = -EAGAIN;if ((file->f_flags & O_NONBLOCK) && fc->connected &&    !request_pending(fc))  //如果是非阻塞方式,则判断队列中有无等待处理请求,无请求则直接返回goto err_unlock;request_wait(fc);        //阻塞等待内核态的请求到了        ......if (!list_empty(&fc->interrupts)) {  //判断是否有中断请求需要发送,有则先发中断请求req = list_entry(fc->interrupts.next, struct fuse_req, intr_entry);return fuse_read_interrupt(fc, req, iov, nr_segs);}req = list_entry(fc->pending.next, struct fuse_req, list);  //从pending队列中获得下一个要发生的请求req->state = FUSE_REQ_READING;list_move(&req->list, &fc->io);  //将请求移动到正在进行IO的队列中in = &req->in;reqsize = in->h.len;/* If request is too large, reply with an error and restart the read */........spin_unlock(&fc->lock);fuse_copy_init(&cs, fc, 1, req, iov, nr_segs);  //为将请求拷贝到用户态做准备err = fuse_copy_one(&cs, &in->h, sizeof(in->h));   //将请求的包头拷贝到用户态if (!err)err = fuse_copy_args(&cs, in->numargs, in->argpages,     (struct fuse_arg *) in->args, 0);  //将请求的包体拷贝到用户态fuse_copy_finish(&cs);  //完成拷贝,释放内存spin_lock(&fc->lock);req->locked = 0;//对发送过程进行错误判断,省略....if (!req->isreply)   //如果没有返回值,则结束请求request_end(fc, req);else {req->state = FUSE_REQ_SENT;  //如果这个请求需要用户态返回执行结果list_move_tail(&req->list, &fc->processing);  //则将请求转到processing队列中,交给fuse_dev_write来处理if (req->interrupted)queue_interrupt(fc, req);spin_unlock(&fc->lock);}return reqsize; err_unlock:spin_unlock(&fc->lock);return err;}

原创粉丝点击