目录
1. 简述
2. 实现原理
3. Linux的零拷贝实现
4. 例程
5. 特别说明
1. 简述
在Linux系统中,零拷贝(Zero-Copy)技术是一种高效的数据传输技术,它允许数据直接在发送方和接收方之间传输,无需在内核和用户空间之间复制数据。这种技术可以显著提高文件传输、网络通信等操作的性能。
2. 实现原理
传统的数据传输方式通常涉及多次数据复制操作:
(1)从磁盘读取数据到内核缓冲区。
(2)从内核缓冲区复制到用户空间缓冲区。
(3)从用户空间缓冲区复制到网络缓冲区。
零拷贝技术通过减少或消除这些复制步骤来提高效率。在零拷贝模型中,数据直接从内核缓冲区传输到目标(如网络栈或另一个文件描述符)。
3. Linux的零拷贝实现
sendfile函数是Linux内核提供的一个系统调用,用于实现高效的文件传输。它允许将文件数据直接发送到套接字,无需在用户空间和内核空间之间复制数据。
其原型如下。
#include <sys/sendfile.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
其中,
out_fd:输出文件描述符,即套接字描述符。
in_fd:输入文件描述符,即要发送的文件描述符。
offset:指向off_t类型的指针,指定从文件的哪个位置开始发送数据。如果为NULL,则从当前文件位置开始发送。
count:要发送的数据量,以字节为单位。
返回值:
成功时,sendfile返回已发送的字节数。
出错时,返回-1,并设置errno以指示错误。
4. 例程
#include <iostream>#include <fstream>#include <sys/socket.h>#include <sys/types.h>#include <netinet/in.h>#include <unistd.h>#include <string.h>#include <sys/sendfile.h>int main(int argc, char* argv[]){/** 服务器地址和端口. */const char* server_ip = "127.0.0.1";int port = 3333;/** 创建套接字. */int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1){perror("create socket failed!");return 1;}/** 设置服务器地址. */struct sockaddr_in serv_addr;memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(port);if (inet_pton(AF_INET, server_ip, &serv_addr.sin_addr) <= 0){perror("inet_pton failed!");close(sockfd);return 1;}/** 连接到服务器. */if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){perror("connect failed!");close(sockfd);return 1;}/** 打开要发送的文件. */const char* filename = "example.dat";int file_fd = open(filename, O_RDONLY);if (file_fd == -1) {perror("open file failed!");close(sockfd);return 1;}/** 使用 sendfile 发送文件. */off_t offset = 0;ssize_t result;while ((result = sendfile(sockfd, file_fd, &offset, 4096)) > 0){std::cout << "Sent " << result << " bytes\n";}if (result == -1){perror("sendfile failed!");}/** 关闭文件描述符. */close(file_fd);/** 关闭套接字. */close(sockfd);return 0;}
5. 特别说明
sendfile 函数设计的初衷是用于将文件数据直接发送到网络套接字,从而实现高效的数据传输,减少数据复制的开销。在传统的使用场景中,sendfile 主要用于以下两种情况:
(1)将一个文件的内容发送到一个网络套接字(如TCP套接字)。
(2)在两个文件描述符之间传输数据,其中一个通常是文件描述符,另一个可以是管道(pipe)或其他类型的文件描述符。
sendfile 的一般用法
在 Linux 中,sendfile 函数通常用于将一个文件描述符(in_fd)中的数据发送到另一个文件描述符(out_fd)。这里的 out_fd 不一定非得是网络套接字,它也可以是其他类型的文件描述符,只要它支持 write 操作。
用在任意文件描述符之间
在某些Linux发行版中,sendfile 函数可以用于任意两个文件描述符之间的数据传输,只要它们都支持 splice 系统调用。splice 是一个用于在两个文件描述符之间传输数据的系统调用,它支持零拷贝操作。从 Linux 内核 2.6.17 开始,splice 函数被用来在内部实现 sendfile。
sendfile 的行为可能会因不同的Linux发行版和内核版本而异。在某些系统上,sendfile 可能只支持文件到套接字的传输。