免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3116 | 回复: 0
打印 上一主题 下一主题

fuse的big_writes与direct_io选项分析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-01-31 21:08 |只看该作者 |倒序浏览

对fuse提供的两个选项direct_io及big_writes困惑已久,以前对内核完全不了解,看不懂fuse内核模块的代码,这两天把fuse的代码重新过了一遍,把整个机制弄清楚了,很多细节方面的东西还在学习中。
指定direct_io挂载文件系统时,系统调用到了fuse层后,会跳过页高速缓存,当指定了direct_io后,读写系统调用会使用fuse_direct_io_file_operations的读写方法。
#fuse/kernel/file.c
static const struct file_operations fuse_direct_io_file_operations = {
          ……
         .read                  = fuse_direct_read,
         .write                 = fuse_direct_write
……      
};

fuse_direct_read/fuse_direct_write都是通过fuse_direct_io实现的
static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
                                                              size_t count, loff_t *ppos, int write)
{
                                        struct inode *inode = file->f_path.dentry->d_inode;
                                        struct fuse_conn *fc = get_fuse_conn(inode);
                                        //使用direct_io每次io量为max_write(max_read)
                                        size_t nmax = write ? fc->max_write : fc->max_read;
                                        ……..
                                        while (count) {
                                               size_t nres;
                                               //请求大小与最大io量对比,取小者
                                               size_t nbytes = min(count, nmax);
                                //如果是写将则将用户空间的数据拷贝进来,作为fuse_req中in字段的值;如果是读,则将用户空间的地址作为fuse_req中out的值
                                               int err = fuse_get_user_pages(req, buf, &nbytes, write);
                    
                                        //根据请求类型将请求发送到/dev/fuse,等待其处理
                                               if (write)
                                                        nres = fuse_send_write(req, file, inode, pos, nbytes,
                                                                                  current->files);
                                               else
                                                        nres = fuse_send_read(req, file, inode, pos, nbytes,
                                                                                 current->files);
                                              …….
}

从上面的代码可以看出,只要fuse每次处理读写请求的大小是受到max_read,max_write参数(挂载文件系统时可以指定)限制的,对于vfs传递下来的请求,读请求只要不超过max_read,则只需要一次fuse的read,写请求只要不超过max_write,则只需要一次fuse的write即可。
另外read,write的处理受限于几个宏定义的值:
fuse/kernel/fuse_i.h
#define FUSE_MAX_PAGES_PER_REQ 32 //fuse每个请求能使用的最大页数
fuse/lib/fuse_kern_chan.c
#define MIN_BUFSIZE 0x21000  //用户态与fuse/dev之间channal的缓冲区大小
从这两个值可以看出,fuse处理请求时能使用的最大空间为128K

在指定direct_io的情况下,使用cp拷贝文件,为什么write只有4k,而read能达到128k?
经多次测试,cp使用的缓冲区应该是4096,所以cp中的read(write)系统调用每次传递个vfs的请求大小为4K,而vfs接受到请求后,对于写请求直接发到fuse,fuse处理写请求;对于读请求,由于linux系统的预读机制,如果是顺序读,预读的最大值能达到128k,这个值在inlcude/linux/mm.h中定义了,即#define VM_MAX_READAHEAD 128(单位是KB)。因此对于cp小的缓冲区,fuse的读请求能达到128k,而write只能是4k。

故使用dd等能指定每次请求大小的工具,并加上direct_io参数,能使达到fuse的读写请求达到128k,要想这个值更高,必须对fuse以及系统内核进行修改,主要就是上面提到的几个限制。
如果想io请求达到1M,可做如下修改。
#define FUSE_MAX_PAGES_PER_REQ  256
#define MIN_BUFSIZE 0x101000   //两者改针对fuse,需要重新编译fuse模块。
#define VM_MAX_READAHEAD 1024  //针对系统内核,将预读的最大值设为1M,需重新编译内核

按照我的理解,使用direct_io选项,并且在用户态指定了请求大小时,只需要改fuse的两个值就行,为什么还需要修改内核的VM_MAX_READAHEAD才能使请求提高到1M呢?

big_writes是怎么回事?
big_writes选项是到fuse-2.8才加的,需要相应fuse内核模块的支持(据fuse的作者说linux-2.6.26以上内核中的fuse才支持。

在以前的fuse内核模块中,对于使用页高速缓存的读写,没有实现writepages方法,即一次只能写一个页,所以在以前中的版本中,不适用direct_io的情况下,写请求最大为4k,我看的是2.6.30内核中的fuse内核代码。

fuse.h中增加FUSE_BIG_WRITES的声明,fuse_i.h里fuse_conn中增加了big_writes字段,在fuse_fill_write_pages中有如下变化。
#in file.c
static ssize_t fuse_fill_write_pages(struct fuse_req *req,
                       struct address_space *mapping,
                       struct iov_iter *ii, loff_t pos)
{
           ……
           do {
            err = 0;
            req->pages[req->num_pages] = page;
            req->num_pages++;
     //如果没有指定big_writes标志,则只fill一页就就跳出循环,而指定该标志后,该值会提高到max_write,FUSE_MAX_PAGES_REQ中限定的较小值。
           if (!fc->big_writes)
                break;
           } while (iov_iter_count(ii) && count max_write &&
             req->num_pages
           return count > 0 ? count : err;
}

同样在指定big_writes的情况下,修改上面提到的三个地方,也可以使请求的值提高到更大,两者的区别在于big_writes使得VFS维护了所读(写)文件的页高速缓存,之后的读写效率会很高,而direct_io每次绕过页高速缓存。

以上是对fuse中两个挂载选项的分析,如有问题,烦请指出。


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

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP