零拷贝原理

本文介绍零拷贝(Zero-Copy)的原理和应用场景。

什么是零拷贝

零拷贝是一种 I/O 操作优化技术…

零拷贝(Zero-Copy)实现原理

  1. 传统方式 vs 零拷贝

    1
    2
    3
    4
    5
    6
    7
    8
    9
    传统方式:read() + write()
    应用程序需要:
    1. read()读取数据到用户空间
    2. write()写入数据到socket缓冲区

    零拷贝:sendfile()
    应用程序:
    1. 直接调用sendfile()系统调用
    2. 内核直接在内核空间完成传输
  2. sendfile()系统调用

    1
    2
    3
    4
    5
    // Linux系统调用
    ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

    // Java NIO中的使用
    FileChannel.transferTo(position, count, socketChannel);
  3. 实现机制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    两种实现方式:

    1. 带DMA收集功能的网卡:
    ┌─────────┐ ┌──────────┐ ┌─────────┐
    │ 磁盘 │ -> │ 内核缓冲区│ => │ 网卡 │
    └─────────┘ └──────────┘ └─────────┘
    DMA拷贝 只传递描述符 DMA拷贝
    (不复制数据)

    2. 普通网卡:
    ┌─────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐
    │ 磁盘 │ -> │ 内核缓冲区│ -> │socket缓冲区│ -> │ 网卡 │
    └─────────┘ └──────────┘ └──────────┘ └─────────┘
    DMA拷贝 CPU拷贝 DMA拷贝
  4. DMA收集功能原理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    核心机制:
    1. 内核缓冲区不复制数据
    2. 只向网卡传递内存描述符(sg_list)
    3. 网卡根据描述符直接从内核缓冲区读取数据

    描述符内容:
    - 内存地址
    - 数据长度
    - 偏移量
  5. 性能对比

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    发送1GB文件:

    传统方式:
    - CPU拷贝:2次
    - DMA拷贝:2次
    - 上下文切换:4次
    - 总耗时:~10秒

    零拷贝(带DMA收集):
    - CPU拷贝:0次
    - DMA拷贝:2次
    - 上下文切换:2次
    - 总耗时:~3秒
  6. Kafka中的应用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // Kafka源码中的使用
    public class FileChannel {
    public long transferTo(long position, long count, WritableByteChannel target) {
    // 底层调用sendfile()
    return transferTo0(position, count, target);
    }
    }

    优势:
    1. 减少CPU使用
    2. 提高吞吐量
    3. 降低内存使用
    4. 减少上下文切换