Deepseek 3FS 高性能API

451 阅读5分钟

一.3FS提供两种方式写数据

image.png

使用标准POSIX接口的方式,则所有读写调用会通过VFS和FUSE转发给3FS客户端,客户端负责处理数据传输: image.png

以下主要是讲3FS高性能API的方式.

二.通过高性能API write()写请求流程

1.用户进程调用3FS的高性能API(如usrbio_alloc)分配Iov内存,然后将数据写入Iov

2.用户进程调用write(fd, buf, count),其中buf指向Iov中的数据地址

3.VFS收到wirte()请求后,将请求转到FUSE 内核模块

4.FUSE内核模块 将写请求发送给用户态fuse客户端

5.FUSE客户端 收到请求后,直接通过共享内存获取iov上的数据

6.FUSE客户端 与存储节点之间已经通过 InfiniBand Verbs API建立了 RDMA 队列对QP用于通信

7.FUSE客户端 使用 RDMA写操作(RDMA_WRITE)将 Iov 中的数据发送到存储节点

8.存储节点的 RDMA 适配器接收到写请求后,直接将数据写入指定的内存区域。

9.存储节点的软件层随后将数据从内存持久化到存储介质

10.RDMA 写操作完成后,FUSE客户端 收到完成通知

11.FUSE客户端 通过FUSE内核模块 将写操作的成功状态通知用户进程

三.设计的优势

3FS采用“应用写入Iov → FUSE客户端从Iov读取 → RDMA从Iov传输到存储节点”的流程,有以下优势:

  • 零拷贝:应用数据写入Iov后,FUSE客户端直接读取,无需额外拷贝。

  • 高效传输:FUSE客户端通过RDMA将Iov数据发送到存储节点,充分利用硬件加速。

  • 统一管理:FUSE客户端负责所有RDMA操作,简化应用开发,优化系统资源。

四.为什么不让应用直接通过RDMA传输?

1.文件系统功能的必要性  

  • 文件系统不仅仅是数据存储,还包括命名空间管理、权限控制、数据一致性和元数据处理。如果应用直接通过RDMA发送数据到存储节点,会绕过这些功能,可能导致:

    • 数据一致性问题:多个应用可能同时写入同一位置,造成冲突。
    • 权限问题:应用可能访问未授权的数据,增加安全风险。
  • FUSE客户端确保所有操作符合文件系统规则,维护数据完整性。

2.分布式存储的复杂性  

  • 3FS是一个分布式文件系统,存储节点可能分布在多个物理位置,涉及负载均衡、数据复制和故障恢复。

  • 如果应用直接通过RDMA传输,需要知道存储节点的地址、端口和数据分布策略,这对应用来说过于复杂。

  • FUSE客户端负责这些管理任务,应用只需通过标准接口操作文件,简化使用。

3.性能与资源管理的优化  

  • RDMA操作需要内存注册(通过ibv_reg_mr),这有一定开销。如果每个应用都直接使用RDMA,可能会导致资源竞争和注册开销增加。

  • FUSE客户端可以预先注册共享内存Iov,供多个应用复用,减少重复开销。

  • 此外,FUSE客户端能批量处理I/O请求,优化RDMA队列对(Queue Pair, QP)的使用,提升整体性能。

4.标准接口与兼容性  

  • 3FS通过FUSE提供POSIX接口,让应用可以用标准系统调用(如write())操作文件,无需修改代码。

  • 如果应用直接通过RDMA传输,需要使用InfiniBand Verbs API或类似库,这会增加开发复杂性,且不兼容现有应用程序。

  • 3FS用户空间API文档来看,3FS的设计目标是易用性与高性能兼顾。

5.安全性和访问控制  

  • FUSE客户端可以根据文件权限限制访问,防止未授权操作。

  • 如果应用直接通过RDMA发送数据,可能绕过这些安全机制,增加数据泄露或篡改风险。

五.Example

#include <hf3fs_usrbio.h>  // 3FS用户态I/O库头文件

// 定义I/O参数:1024次I/O操作,每个块32MB
constexpr uint64_t NUM_IOS = 1024;
constexpr uint64_t BLOCK_SIZE = (32 << 20);

int main() {
    // 创建I/O请求上下文 (I/O Request)
    struct hf3fs_ior ior;
    hf3fs_iorcreate4(&ior,         // 输出参数:I/O请求上下文
                    "/hf3fs/mount/point",  // 挂载点路径
                    NUM_IOS,       // 最大并发I/O数
                    true,          // 是否启用异步模式
                    0, 0,          // 保留参数
                    -1,            // NUMA节点(-1表示自动选择)
                    0);            // 标志位

    // 创建I/O向量管理 (I/O Vector)
    struct hf3fs_iov iov;
    hf3fs_iovcreate2(&iov,         // 输出参数:I/O向量管理
                    "/hf3fs/mount/point",  // 挂载点路径
                    NUM_IOS * BLOCK_SIZE, // 总预分配空间
                    0,             // 对齐要求
                    -1,            // NUMA节点
                    nullptr);      // 回调函数

    // 打开文件并注册到3FS
    int fd = open("/hf3fs/mount/point/example.bin", O_RDONLY);
    hf3fs_reg_fd(fd, 0);  // 注册文件描述符到3FS(0表示默认选项)

    // 准备批量读取请求
    for (int i = 0; i < NUM_IOS; i++) {
        hf3fs_prep_io(&ior,        // I/O请求上下文
                     &iov,         // I/O向量管理
                     true,         // 是否读取操作(false为写入)
                     iov.base + i * BLOCK_SIZE, // 目标内存地址
                     fd,           // 文件描述符
                     i * BLOCK_SIZE, // 文件偏移量
                     BLOCK_SIZE,   // 传输长度
                     nullptr);     // 回调函数
    }
    
    // 提交所有I/O请求到存储系统
    hf3fs_submit_ios(&ior);

    // 等待所有I/O完成
    hf3fs_cqe cqes[NUM_IOS];  // 完成事件数组
    hf3fs_wait_for_ios(&ior,   // I/O请求上下文
                      cqes,    // 输出完成事件
                      NUM_IOS, // 最小完成数
                      NUM_IOS, // 最大完成数
                      nullptr); // 超时设置

    // 清理资源
    hf3fs_dereg_fd(fd);  // 注销文件描述符
    close(fd);           // 关闭文件
    hf3fs_iovdestroy(&iov); // 销毁I/O向量
    hf3fs_iordestroy(&ior); // 销毁I/O请求上下文

    return 0;
}