linux提供的零拷贝技术
Linux 提供了多种 零拷贝(Zero-Copy) 技术,主要用于 减少 CPU 开销、提高数据传输效率,适用于 高性能网络传输、文件 I/O 等场景。以下是 Linux 常见的 零拷贝技术:
1. sendfile()
🔹 适用于:文件传输(文件 → 套接字)
🔹 原理:
- 直接将文件从 内核缓冲区 发送到 Socket,避免用户态与内核态的额外拷贝。
🔹 传统文件传输(无零拷贝)
read()
:从磁盘读取数据到 内核缓冲区(Page Cache)。- 拷贝到用户态缓冲区。
write()
:再拷贝到 Socket 缓冲区。- 发送到网卡。
🔹 使用 sendfile()
的零拷贝方式
- 从磁盘读取数据到内核缓冲区。
- 直接从内核缓冲区发送到 Socket(避免用户态拷贝)。
- 数据传输到网卡。
🔹 示例代码
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_uring
是 Linux 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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步