高级I/O函数之sendfile函数[通俗易懂]

高级I/O函数之sendfile函数[通俗易懂]sendfile函数在两个文件描述符之间传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,被称为零拷贝。函数定义为:#include<sys/sendfile.h>ssize_tsenfile(intout_fd,intin_fd,off_t*offset,size_tcount);in_fd参数是待读出内容的文件描述符,out…

大家好,又见面了,我是你们的朋友全栈君。

sendfile函数在两个文件描述符之间传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,被称为零拷贝。函数定义为:

#include<sys/sendfile.h>
ssize_t senfile(int out_fd,int in_fd,off_t* offset,size_t count);

in_fd参数是待读出内容的文件描述符,out_fd参数是待写入内容的文件描述符。offset参数指定从读入文件流的哪个位置开始读,如果为空,则使用读入文件流默认的起始位置。count参数指定文件描述符in_fd和out_fd之间传输的字节数。

in_fd必须是一个支持类似mmap函数的文件描述符,即它必须指向真实的文件,不能是socket和管道,而out_fd必须是一个socket

首先我们来看看传统的read/write方式进行socket的传输。
当需要对一个文件进行传输的时候,具体流程细节如下:

1:调用read函数,文件数据copy到内核缓冲区

2:read函数返回,文件数据从内核缓冲区copy到用户缓冲区

3:write函数调用,将文件数据从用户缓冲区copy到内核与socket相关的缓冲区

4:数据从socket缓冲区copy到相关协议引擎。

在这个过程中发生了四次copy操作。

硬盘->内核->用户->socket缓冲区(内核)->协议引擎。

而sendfile的工作原理呢??

1、系统调用 sendfile() 通过 DMA 把硬盘数据拷贝到 kernel buffer,然后数据被 kernel 直接拷贝到另外一个与 socket 相关的 kernel buffer。这里没有 用户态和核心态 之间的切换,在内核中直接完成了从一个 buffer 到另一个 buffer 的拷贝。
2、DMA 把数据从 kernel buffer 直接拷贝给协议栈,没有切换,也不需要数据从用户态和核心态,因为数据就在 kernel 里。

测试代码:

#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
#include<stdio.h>
#include<string.h>
#include<sys/sendfile.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<errno.h>

int main(int argc,char *argv[])
{
    if(argc<=3)
    {
        printf("usage:%s ip_address port_number filename\n",basename(argv[0]));
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);
    const char* file_name = argv[3];

    int filefd = open(file_name,O_RDONLY);
    assert(filefd>0);

    struct stat stat_buf;
    fstat(filefd,&stat_buf);

    struct sockaddr_in address;

    bzero(&address,sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET,ip,&address.sin_addr);
    address.sin_port = htons(port);

    int sock = socket(PF_INET,SOCK_STREAM,0);
    assert(sock>=0);

    int ret = bind(sock,(struct sockaddr*)&address,sizeof(address));
    assert(ret!=-1);

    ret = listen(sock,5);
    assert(ret!=-1);

    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof(client);

    int connfd = accept(sock,(struct sockaddr*)&client,&client_addrlength);

    if(connfd<0)
    {
        printf("errno is %d\n",errno);
    }
    else 
    {
        sendfile(connfd,filefd,NULL,stat_buf.st_size);
        close(connfd);
    }
    close(sock);
    return 0;
}

然后进行
这里写图片描述

在另外一个虚拟机上telnet

这里写图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/143051.html原文链接:https://javaforall.net

(0)
上一篇 2022年5月10日 下午10:20
下一篇 2022年5月10日 下午10:40


相关推荐

  • 蓝耘智算平台上3分钟快速部署 DeepSeek-R1教程(零基础版教程:附全程截图)

    蓝耘智算平台上3分钟快速部署 DeepSeek-R1教程(零基础版教程:附全程截图)

    2026年3月16日
    3
  • idea搭建vue(使用VUE打开一个项目)

    使用IDEA创建咱们的第一个VUE项目最近在学习VUE,想着使用vscode、hbulider、webstorm这些软件学习,但听前端前辈们说要很多插件什么的等等等!听完咱还是选择IDEA吧,毕竟和IDEA还是很亲近的!1.安装环境–让VUE‘顺产’(1)安装node.js至于为什么安装大家可以看看前辈们的博客哦!1.先让IDAE准备准备(迎接VUE)~~~提示:这里可以添加要学的内容例如:1、搭建Java开发环境2、掌握Java基本语法3、掌握条件语句4、掌握循环

    2022年4月14日
    299
  • [已解决]踩过的坑之mysql连接报“Communications link failure”错误

    [已解决]踩过的坑之mysql连接报“Communications link failure”错误目录前言第一种方法:第二种方法第三种方法(适用于项目和数据库在同一台服务器)第四种方法第五种方法(项目和数据库不在同一台服务器)总结前言先给大家简述一下我的坑吧,(我用的是mysql,至于oracle有没有这样的问题,有心的小伙伴们可以测试一下哈),在自己做个javaweb测试项目的时候,因为买的是云服务器,所以数据库连接的是用ip地址,用IDE开发好…

    2022年5月18日
    58
  • linux修改用户名的命令_linux修改用户名和密码

    linux修改用户名的命令_linux修改用户名和密码centos7修改已存在用户的用户名目标:将centos用户更名为laizili第一步:登出要修改用户名的用户(没有注销登录的用户无法修改)第二步:以root身份登录终端;进行如下操作:vim/etc/passwd…

    2026年1月20日
    8
  • nginx部署多个服务

    nginx部署多个服务众所周知 Nginx enginex 是一个高性能的 HTTP 和反向代理服务 也是一个 IMAP POP3 SMTP 服务 Nginx 作为负载均衡服务 Nginx 既可以在内部直接支持 Rails 和 PHP 程序对外进行服务 也可以支持作为 HTTP 代理服务对外进行服务 这样说你可能还不理解 但是如果我说 Apache 你肯定知道 nginx 就是类似与 Apache 这样的服务器 nginx 相对于

    2026年3月19日
    1
  • Linux内核版本介绍与查询

    Linux内核版本介绍与查询文章目录 Linux 内核 Linuxkernel 简介 Linux 内核版本号 1 在 CentOS 下如 2 在 Ubuntu 下如 3 在 ARMCortex A7 内核的嵌入式 Linux 开发板下内核版本分类查看 Linux 内核版本命令查看 Linux 系统版本的命令本文作者 Jasonhu 本文链接 http jasonhzy github io 2019 02 05 linux kernel version Linux 内核 Linuxkernel 简介 Linux 内核版本命名在不同时期有着不同的规范

    2026年3月18日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号