linux提供的零拷贝技术

Linux 提供了多种 零拷贝(Zero-Copy) 技术,主要用于 减少 CPU 开销、提高数据传输效率,适用于 高性能网络传输、文件 I/O 等场景。以下是 Linux 常见的 零拷贝技术


1. sendfile()

🔹 适用于:文件传输(文件 → 套接字)
🔹 原理:

  • 直接将文件从 内核缓冲区 发送到 Socket,避免用户态与内核态的额外拷贝。

🔹 传统文件传输(无零拷贝)

  1. read():从磁盘读取数据到 内核缓冲区(Page Cache)
  2. 拷贝到用户态缓冲区
  3. write():再拷贝到 Socket 缓冲区
  4. 发送到网卡

🔹 使用 sendfile() 的零拷贝方式

  1. 从磁盘读取数据到内核缓冲区
  2. 直接从内核缓冲区发送到 Socket(避免用户态拷贝)。
  3. 数据传输到网卡

🔹 示例代码

int file_fd = open("test.txt", O_RDONLY);
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
sendfile(sock_fd, file_fd, NULL, file_size); // 直接从文件到 Socket

减少两次 CPU 拷贝,提高传输效率! 🚀


2. mmap() + write()

🔹 适用于:文件 I/O(文件 → 用户进程 → Socket)
🔹 原理:

  • 通过 mmap() 将文件映射到 用户态地址空间,减少 read() 调用的 用户态拷贝,但仍需要 write() 将数据从用户态发送到 Socket。

🔹 传统方式:

int fd = open("test.txt", O_RDONLY);
char buffer[4096];
read(fd, buffer, sizeof(buffer));
write(socket_fd, buffer, sizeof(buffer)); // 需要额外拷贝

🔹 mmap() 方式(减少一次拷贝)

void *mapped = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, file_fd, 0);
write(socket_fd, mapped, file_size); // 直接从 mmap 映射区发送
munmap(mapped, file_size);

避免 read() 额外拷贝,减少 CPU 消耗! 🚀


3. splice()

🔹 适用于:管道传输(文件 → 管道 → 套接字)
🔹 原理:

  • 允许 两个文件描述符之间 直接传输数据,而不经过用户态,提高数据传输效率。
  • 常用于 文件 → Socket、Socket → Socket 传输,如 网络代理、文件服务器 等。

🔹 示例:从文件直接发送到网络

int pipefd[2];
pipe(pipefd);
splice(file_fd, NULL, pipefd[1], NULL, file_size, SPLICE_F_MOVE);
splice(pipefd[0], NULL, socket_fd, NULL, file_size, SPLICE_F_MOVE);

适用于管道数据流,避免用户态拷贝,提高吞吐量! 🚀


4. vmsplice()

🔹 适用于:用户态内存数据 → 管道 → 套接字
🔹 原理:

  • vmsplice() 允许 将用户态的缓冲区直接映射到内核管道,减少用户态 → 内核态拷贝。
  • 类似 splice(),但 vmsplice() 作用于 用户空间 缓冲区,而 splice() 作用于 文件描述符

🔹 示例:用户态数据发送到管道

struct iovec iov;
iov.iov_base = user_buffer;
iov.iov_len = buffer_size;
vmsplice(pipefd[1], &iov, 1, 0); // 直接将用户缓冲区映射到管道

减少用户态数据拷贝,提高管道传输效率! 🚀


5. TCP Zero Copy (MSG_ZEROCOPY)

🔹 适用于:网络传输(应用缓冲区 → TCP)
🔹 原理:

  • MSG_ZEROCOPY 允许 send() 直接使用用户态缓冲区,避免 额外拷贝到 Socket 发送缓冲区,适用于 大规模数据传输
  • 适用于 Linux 4.14+ 版本,但仅适用于 支持 DMA 的网卡(如高性能网络设备)。

🔹 示例:

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
send(sockfd, user_buffer, buffer_size, MSG_ZEROCOPY); // 直接发送用户态数据

减少数据拷贝,提高 TCP 高吞吐量传输性能! 🚀


6. io_uring(Linux 5.1+)

🔹 适用于:异步 I/O(文件、Socket、磁盘 I/O)
🔹 原理:

  • io_uringLinux 5.1 引入的新一代 异步 I/O 机制,避免内核与用户态之间的频繁切换。
  • 通过 共享环形缓冲区,减少系统调用,提高 I/O 性能

🔹 示例:使用 io_uring 发送文件

io_uring_queue_init(32, &ring, 0);
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
io_uring_prep_send(sqe, sockfd, buffer, buffer_size, 0);
io_uring_submit(&ring);

提供真正的零拷贝异步 I/O,性能比 sendfile() 更强! 🚀


总结:Linux 零拷贝技术对比

技术 适用场景 减少的拷贝次数 适用版本
sendfile() 文件 → Socket 2 次 Linux 2.1+
mmap() + write() 文件 → Socket 1 次 Linux 2.1+
splice() 文件/管道 → Socket 2 次 Linux 2.6.17+
vmsplice() 用户缓冲区 → 管道 1 次 Linux 2.6.17+
MSG_ZEROCOPY TCP 传输 1 次 Linux 4.14+
io_uring 异步文件/Socket I/O 2 次 Linux 5.1+

💡 最佳选择

  • 高效文件传输sendfile()(兼容性好,性能高)
  • 管道数据流处理splice()(高吞吐)
  • 用户态数据直接发送MSG_ZEROCOPY(适用于 TCP 传输)
  • 最先进的方案io_uring(适用于现代异步 I/O)

本文作者:MuXinu

本文链接:https://www.cnblogs.com/MuXinu/p/18711921

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   MuXinu  阅读(23)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起