Linux应用开发(14):Linux系统零拷贝技术(sendfile)

目录

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 可能只支持文件到套接字的传输。

  • 15
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值