零拷贝技术

参考:零拷贝技术视频

参考:零拷贝技术文章

一、传统IO执行流程

在这里插入图片描述
1.用户应用进程调用read函数,向操作系统发起IO调用,上下文从用户态转为内核态(切换1)
2.DMA控制器发起I/O请求,把数据从磁盘中,拷贝到内核缓冲区,DMA发出数据读完信号。
3.CPU把内核缓冲区数据,拷贝到用户应用缓冲区
4.read()调用结束数据返回,内核态切换回用户态

5.用户应用进程通过write函数,发起IO调用,上下文从用户态转为内核态
6.CPU把用户缓冲区数据,拷贝到socket缓冲区
7.DMA发出IO请求,把数据从socket缓冲区,拷贝到网卡设备,DMA发出数据写完信号
8.write()调用结束数据返回,内核态切换为用户态

从流程图可以看出,传统IO的读写流程,包括了4次上下文切换(4次用户态和内核态的切换),4次数据拷贝(两次CPU拷贝以及两次的DMA拷贝)

二、mmap+write实现零拷贝

在这里插入图片描述
原理:将内核读缓冲区(PageCache)和用户空间的缓冲区域进行映射,是所有的IO都在内核完成
1.用户进程通过mmap方法向操作系统内核发起IO调用,上下文从用户态切换为内核态
2.CPU利用DMA控制器,把数据从硬盘中拷贝到内核缓冲区。
3.mmap调用返回,上下文从内核态切换回用户态

4.用户进程通过write方法向操作系统内核发起IO调用,上下文从用户态切换为内核态
5.CPU将内核缓冲区的数据拷贝到的socket缓冲区。
6.CPU利用DMA控制器,把数据从socket缓冲区拷贝到网卡,上下文从内核态切换回用户态,write调用返回。

可以发现,mmap+write实现的零拷贝,I/O发生了4次用户空间与内核空间的上下文切换,以及3次数据拷贝。其中3次数据拷贝中,包括了2次DMA拷贝和1次CPU拷贝。

三、sendfile实现的零拷贝

sendfile是Linux2.1内核版本后引入的一个系统调用函数

sendfile表示在两个文件描述符之间传输数据,它是在操作系统内核中操作的,避免了数据从内核缓冲区和用户缓冲区之间的拷贝操作,因此可以使用它来实现零拷贝。
在这里插入图片描述
1.用户进程发起sendfile系统调用,上下文(切换1)从用户态转向内核态
2.DMA控制器,把数据从硬盘中拷贝到内核缓冲区。
3.CPU将读缓冲区中数据拷贝到socket缓冲区
4.DMA控制器,异步把数据从socket缓冲区拷贝到网卡,
5.上下文(切换2)从内核态切换回用户态,sendfile调用返回。

可以发现,sendfile实现的零拷贝,I/O发生了2次用户空间与内核空间的上下文切换,以及3次数据拷贝。其中3次数据拷贝中,包括了2次DMA拷贝和1次CPU拷贝

四、代码演示

mmap+write演示代码:

#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <iostream>
#include <sys/stat.h>
#include <cstring>

int main()
{
    // 打开源文件
    int src_fd = open("source.txt", O_RDONLY);
    if (src_fd == -1)
    {
        std::cerr << "Error opening source file." << std::endl;
        return 1;
    }

    // 获取源文件大小
    struct stat sb;
    if (fstat(src_fd, &sb) == -1)
    {
        std::cerr << "Error getting file size." << std::endl;
        return 1;
    }
    size_t file_size = sb.st_size;

    // 内存映射源文件
    void *mapped_file = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, src_fd, 0);
    if (mapped_file == MAP_FAILED)
    {
        std::cerr << "Error mapping file to memory." << std::endl;
        return 1;
    }

    // 打开目标文件
    int dest_fd = open("destination.txt", O_WRONLY | O_CREAT, 0644);
    if (dest_fd == -1)
    {
        std::cerr << "Error opening destination file." << std::endl;
        munmap(mapped_file, file_size); // 解除映射
        return 1;
    }

    // 将映射的文件内容写入目标文件
    ssize_t bytes_written = write(dest_fd, mapped_file, file_size);
    if (bytes_written != file_size)
    {
        std::cerr << "Error writing to destination file." << std::endl;
        munmap(mapped_file, file_size); // 解除映射
        return 1;
    }

    // 清理
    munmap(mapped_file, file_size); // 解除映射
    close(src_fd);
    close(dest_fd);

    std::cout << "File copied successfully." << std::endl;
    return 0;
}

sendfile演示代码:

#include <fcntl.h>
#include <sys/sendfile.h>
#include <unistd.h>
#include <iostream>
#include <sys/stat.h>

int main()
{
    // 打开源文件
    int src_fd = open("source.txt", O_RDONLY);
    if (src_fd == -1)
    {
        std::cerr << "Error opening source file." << std::endl;
        return 1;
    }

    // 打开目标文件
    int dest_fd = open("destination.txt", O_WRONLY | O_CREAT, 0644);
    if (dest_fd == -1)
    {
        std::cerr << "Error opening destination file." << std::endl;
        close(src_fd);
        return 1;
    }

    off_t offset = 0; // 从源文件的开始位置读取
    size_t count = 0; // 不限制每次读取的字节数量,sendfile() 将自动决定
    ssize_t transferred;

    // 使用 sendfile() 进行文件传输
    while ((transferred = sendfile(dest_fd, src_fd, &offset, count)) > 0)
    {
        std::cout << "Transferred: " << transferred << " bytes" << std::endl;
    }

    if (transferred == -1)
    {
        std::cerr << "Error during sendfile operation." << std::endl;
        close(src_fd);
        close(dest_fd);
        return 1;
    }

    // 清理
    close(src_fd);
    close(dest_fd);

    std::cout << "File copied successfully." << std::endl;
    return 0;
}

相关推荐

  1. 拷贝技术

    2024-07-19 15:28:02       58 阅读
  2. 什么是拷贝

    2024-07-19 15:28:02       30 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-19 15:28:02       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-19 15:28:02       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-19 15:28:02       58 阅读
  4. Python语言-面向对象

    2024-07-19 15:28:02       69 阅读

热门阅读

  1. celery config_from_object的简单使用

    2024-07-19 15:28:02       21 阅读
  2. python程序设定定时任务

    2024-07-19 15:28:02       19 阅读
  3. 【电子数据取证】从SSH开始使用Linux

    2024-07-19 15:28:02       22 阅读
  4. axios源码分析与模拟(上)

    2024-07-19 15:28:02       20 阅读
  5. c语言(7.19)

    2024-07-19 15:28:02       22 阅读
  6. 关于UniApp使用的个人笔记

    2024-07-19 15:28:02       18 阅读
  7. Qt之基础体系

    2024-07-19 15:28:02       15 阅读
  8. Git笔记

    Git笔记

    2024-07-19 15:28:02      20 阅读