当前位置: 首页 > article >正文

Linux 零拷贝技术

一、传统做法,经历“四次拷贝”

数据 1.读取到内核缓冲区 2.拷贝到用户缓冲区 3.写入到内核缓冲区 4.拷贝到网卡
传统数据传输“四次拷贝”

使用 DMA,减少2次拷贝,还剩2次拷贝

DMA 负责硬盘到内核缓冲区和内核到网卡的传输。
CPU 仍需处理内核和用户缓冲区之间的数据传输。
DMA减少2次拷贝

二、Linux 四种零拷贝技术

需硬件支持 DMA (Direct Memory Access,直接内存访问) 一种让数据在硬盘和内存之间直接传输的技术,不需要 CPU 逐字节参与

方法描述CPU 参与度适用场景
sendfile直接发送文件数据到套接字,无需拷贝到用户空间极少,数据直接传输文件服务器、视频流传输等大文件场景
splice在内核空间内高效地在文件描述符之间传输数据。极少,完全在内核内文件、管道与 socket 之间的复杂传输场景
mmap + write映射文件到内存,用 write 发送数据,灵活处理数据中等,需要映射和写入数据需要处理或修改的场景,如压缩加密
tee将管道中的数据复制到另一个管道,无需消耗原始数据。极少,内核中数据复制日志处理、实时数据监控等多目标场景

Linux 零拷贝技术

2.1 sendfile 文件式零拷贝

sendfile 最早引入,专为高效传输大文件的情况。例如文件服务器流媒体传输备份系统等。
sendfile零拷贝

int input_fd = open("input.txt", O_RDONLY);
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
bind(server_fd, (struct sockaddr *)&address, sizeof(address));
listen(server_fd, 3);
int client_fd = accept(server_fd, NULL, NULL);
//
sendfile(client_fd, input_fd, NULL, 1024);
//
close(input_fd);
close(client_fd);
close(server_fd);

2.2 splice 管道式零拷贝

在不同类型文件描述符之间高效直接移动数据。实现复杂数据流向控制。
例如从文件到网络 socket 的传输,或在文件、管道和 socket 之间传递数据
在这里插入图片描述

int input_fd = open("input.txt", O_RDONLY);
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
bind(server_fd, (struct sockaddr *)&address, sizeof(address));
listen(server_fd, 3);
int client_fd = accept(server_fd, NULL, NULL);
//
splice(input_fd, NULL, client_fd, NULL, 1024, SPLICE_F_MORE);
//
close(input_fd);
close(client_fd);
close(server_fd);

2.3 mmap + write:映射式零拷贝

提供了更大的灵活性,允许在用户态访问数据内容。
适用于发送数据前对文件进行预处理的场景(如压缩、加密或者数据转换等)
使用 mmap 将文件数据映射到进程的虚拟地址空间,避免显式的数据拷贝。
通过 write 直接将映射的内存区域数据发送到目标文件描述符(如网络 socket)

在这里插入图片描述

int input_fd = open("input.txt", O_RDONLY);
struct stat file_stat;
fstat(input_fd, &file_stat);
// 映射
char *mapped = mmap(NULL, file_stat.st_size, PROT_READ, MAP_PRIVATE, input_fd, 0);

int server_fd = socket(AF_INET, SOCK_STREAM, 0);
bind(server_fd, (struct sockaddr *)&address, sizeof(address));
listen(server_fd, 3);
int client_fd = accept(server_fd, NULL, NULL);
write(client_fd, mapped, file_stat.st_size);
//
munmap(mapped, file_stat.st_size);
close(input_fd);
close(client_fd);
close(server_fd);

2.4 tee: 复制式零拷贝

把一个管道中数据复制到另一管道,同时保留原管道中数据。这意味着数据可以同时被发送到多个目标,且不影响原来的数据流,
非常适合日志记录实时数据分析等需要把同样的数据送往不同地方的场景

int pipe_fd[2];
pipe(pipe_fd);

int server_fd = socket(AF_INET, SOCK_STREAM, 0);
bind(server_fd, (struct sockaddr *)&address, sizeof(address));
listen(server_fd, 3);
int client_fd = accept(server_fd, NULL, NULL);
// 使用 tee 复制数据
tee(pipe_fd[0], pipe_fd[1], 1024, 0); //复制管道中的数据
splice(pipe_fd[0], NULL, client_fd, NULL, 1024, SPLICE_F_MORE); //发送到socket

close(pipe_fd[0]);
close(pipe_fd[1]);
close(client_fd);
close(server_fd);

http://www.kler.cn/a/533846.html

相关文章:

  • 编程之路:在细节中磨砺技艺
  • 车载软件架构 --- 基于AUTOSAR软件架构的ECU开发流程小白篇
  • 快速幂,错位排序笔记
  • Verilog基础(三):过程
  • LabVIEW的智能电源远程监控系统开发
  • macos系统jmap执行异常
  • C#结合html2canvas生成切割图片并导出到PDF
  • DeepSeek R1技术报告关键解析(9/10):强化学习也不是万能的
  • 2021.3.1的android studio版本就很好用
  • 备考蓝桥杯8——EEPROM读写
  • 深度探索 C 语言操作符:从基础到实战应用
  • 01-两数之和
  • FinDKG: Dynamic Knowledge Graphs...... 基于大语言模型的动态知识图谱论文笔记
  • 2502vim,vim文本对象中文文档
  • ORB-SLAM2源码学习:KeyFrame.cc④: void KeyFrame::UpdateBestCovisibles更新最佳共视
  • HTML-表格,表单标签
  • GitHub Copilot 越狱漏洞
  • VMware Workstation Pro安装了Ubuntu 24.04实现与Windows10之间的复制粘贴
  • linux——网络(服务器的永久不挂——守护进程)
  • MyBatis持久层框架
  • 理解 Maven 的 pom.xml 文件
  • 验证工具:VCS概识
  • Sqli-labs靶场实录(一):Basic Challenges
  • Ubuntu 下 nginx-1.24.0 源码分析 - ngx_sprintf_str 函数
  • 蓝桥杯思维训练(五)
  • 【Day31 LeetCode】动态规划DP Ⅳ