ILD

libfuse splice support
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2025-2-12 站点:Inside Linux Development

libfuse支持splice,来实现文件读写时,fuse内核模块和fuse server的零拷贝支持。

只有读写涉及到大缓存,才需要零拷贝。其它的消息如unlink等,显然不需要splice。


fuse还支持passthrough,passthrough模式和splice不一样。

passthrough直接将一个fd传递给fuse内核模块。这样文件读写,在内核直接操作此fd。这样就不需要和fuse server交互了。

splice()

首先来了解一下splice()这个系统调用。


       #define _GNU_SOURCE         /* See feature_test_macros(7) */

       #include <fcntl.h>


       ssize_t splice(int fd_in, loff_t *off_in, int fd_out,

                      loff_t *off_out, size_t len, unsigned int flags);


splice将数据从一个描述符移动到另外一个描述符。它有个要求是至少有一个描述符要求是pipe。这可能是内核数据格式的限制导致的。


write

write是写入一个缓存到fuse内核模块,然后fuse内核模块发送给fuse server。

fuse通信是通过fd的,没开启splice的时候,fuse server直接从fd读取消息和数据。消息和数据是融合在一起的。


fuse处理消息的流程是先收取消息,然后处理:

fuse_session_receive_buf(fs->se, fs->buf);

fuse_session_process_buf(fs->se, fs->buf);


fuse_session_receive_buf:

1 要通过splice处理write,首先需要创建一个pipe。这个pipe缓存在thread local中。

        struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key);

        if (llp == NULL) {

            llp = malloc(sizeof(struct fuse_ll_pipe));

            fuse_pipe(llp->pipe);

        }


2 获得管道后,就将fuse fd的内容移动到管道,并且可以返回数据的长度。

        res =  splice(ch ? ch->fd : se->fd, NULL, llp->pipe[1], NULL, bufsize, 0);


fuse_session_process_buf:

1 首先需要把头部数据读取出来,这样才知道是啥消息

        头部数据的长度是:fuse头部 + write头部

        res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);


2 如果是fuse_write消息,则调用:

        do_write_buf(req, in->nodeid, inarg, buf);

        如果是其他消息,则需要继续读完管道中的消息。


read

read需要fuse server返回一块数据给fuse内核模块。对于大数据,可以使用splice。


提供一个fd和offset,调用fuse_reply_data,即可:

        buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;

        buf.buf[0].fd = fd;

        buf.buf[0].pos = off;


        fuse_reply_data(req_t, &buf, FUSE_BUF_SPLICE_MOVE);


libfuse还支持vmsplice,直接将用户态缓存移动到管道,再移动到fuse fd。


Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.