高级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

这里写图片描述

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • CentOS 7系统离线安装gcc,gcc-c++,让你摆脱联网装不了的困惑!

    CentOS 7系统离线安装gcc,gcc-c++,让你摆脱联网装不了的困惑!我的就是CentOs7系统,需要装一个Nginx服务器的时候,死活都装不上运行环境gcc,gcc-c++,一直纠结了几天,后来我睡觉的时候突然一个灵感来了,就继续查资料,如果缺少依赖包,那么就去装依赖包,手动安装或者离线安装都可以。第二天上班的时候我就开始查网上查资料,果然有这种解决办法,并且还有各个依赖包都抽离出来了,感谢各位大佬的分享,我写这个不是为了宣传什么,…

  • 设计模式–代理模式(Proxy Pattern)

    设计模式–代理模式(Proxy Pattern)

  • SpringBoot 热部署[通俗易懂]

    SpringBoot 热部署[通俗易懂]SpringBoot热部署的作用在实际开发过程中,每次修改代码就得将项目重启,重新部署,对于一些大型应用来说,重启时间需要花费大量的时间成本。而SpringBoot中启用热部署后就变成了非常简单的一件事,因为SpringBoot为我们提供了一个非常方便的工具spring-boot-devtools,我们只需要把这个工具引入到工程里就OK了。作用当项目是启动状态时,修改静态文件并保存后,不需要重启项目来达到使静态文件生效的目的。注意:如果页面包含java代码等后台代码,那么修改这些后台代码热部署就不

    2022年10月25日
  • BS和CS对比_CS与BS对接

    BS和CS对比_CS与BS对接BS和CS对比牛腩新闻发布系统已经开始了不短的时间了,CS的项目也算是接触了下,接下来对比下CS和BS       CS和BS是什么?       C/S结构即Client/Server(客户机/服务器)结构,是大家熟知的软件系统体系结构,通过将任务合理分配到Client端和Server端,降低了系统的通讯开销(客户端和服务器端的通信),可以充分利用两端硬件环境的优势。B/S结

    2022年10月16日
  • bootstrap-fileinput文件上传&后台接收处理

    bootstrap-fileinput文件上传&后台接收处理使用bootstrap-fileinput进行文件上传,bootstrap提供了吩咐的API属性、方法、事件;控制文件上传每一个环境,提供各个部分的`class`。Java上传图片处理以及nodeJS上传文件处理。

发表回复

您的电子邮箱地址不会被公开。

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