sendfile函数–零拷贝

sendfile函数–零拷贝零拷贝:零拷贝技术可以减少数据拷贝和共享总线操作的次数,消除通信数据在存储器之间不必要的中间拷贝过程,有效地提高通信效率,是设计高速接口通道、实现高速服务器和路由器的关键技术之一。sendfile#includessize_tsendfile(intout_fd,intin_fd,off_t*offset,size_tcount);参数特别注意

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

零拷贝:零拷贝技术可以减少数据拷贝和共享总线操作的次数,消除通信数据在存储器之间不必要的中间拷贝过程,有效地提高通信效率,是设计高速接口通道、实现高速服务器和路由器的关键技术之一。
sendfile

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

参数特别注意的是:in_fd必须是一个支持mmap函数的文件描述符,也就是说必须指向真实文件,不能使socket描述符和管道。
out_fd必须是一个socket描述符.
由此可见sendfile几乎是专门为在网络上传输文件而设计的。
out_fd:已经打开了,用于写操作的文件描述符
in_fd:已经打开了,用于读操作的文件描述符
offset:偏移量:表示sendfile函数从in_fd中的哪一偏移量开始读取数据,如果是0表示从文件的开始读,否则从相应的偏移量读取,如果是循环读取的时候,下一次offset值应为sendfile函数返回值加上本次的offset的值。
count:是在两个描述符之间拷贝的字节数
返回值:
如果成功的拷贝,返回写操作到out_fd的字节数,错误返回-1,并相应的设置error信息。

关于sendfile与read和write的比较
服务器响应一个http请求的步骤如下:
1.把磁盘文件读入内核缓冲区
2.从内核缓冲区读到内存
3.处理(静态资源不需要处理)
4.发送到网卡的内核缓冲区(发送缓存)
5.网卡发送数据
而sendfile系统调用,省略了2,3步,磁盘文件被直接发送到了网卡的内存缓冲区,减少了数据复制和内核态切换的开销。
sendfile一直都在核心态进行

普通的read和write的传统网络传输过程的步骤

read(file, tmp_buf, len);
write(socket, tmp_buf, len);
硬盘 >> kernel buffer >> user buffer >> kernel socket buffer >> 协议栈

一般的网络应用是通过读硬盘数据,然后写数据到socket来完成网络传输的。
底层实现如下:
1.系统调用read()产生一个上下文切换:从用户模式切换到内核模式,然后DMA(直接内存存取)执行拷贝,把文件数据从硬盘读到一个内核缓冲区里面去。
2.数据从内核缓冲区拷贝到用户态缓冲区,然后系统调用read()返回,这时又产生一个上下文切换:从内核状态切换到用户态。
3.系统调用write()产生一个上下文切换:从用户态切换到内核态,然后把步骤2中读到用户缓冲区的数据拷贝到核心态缓冲区(数据第二次拷贝到核心态缓冲区),不过这次是个不同的核心态缓冲区,这个缓冲区和socket相关联。
4.系统调用write()返回,产生一个上下文切换:从内核态切换到用户态(第4次切换),然后DMA从内核缓冲区拷贝数据到协议栈(第四次拷贝)。

上述4个步骤,4次上下文切换,4次拷贝,我们发现减少他的切换和拷贝次数,将有效提高性能。这也是sendfile提高性能的方法。

关于sendfile进行网络传输的过程

sendfile(socket, file, len);
硬盘 >> kernel buffer (快速拷贝到kernel socket buffer) >> 协议栈

1.系统调用sendfile()通过DMA把硬盘数据拷贝到内核缓冲区,然后数据直接拷贝到另一个与socket相关的内核缓冲区。(区别)这里没有用户态和核心态之间的切换,在核心态中直接完成了从一个缓冲区到另一个缓冲区的拷贝。
2.DMA把数据从内核缓冲区直接拷贝给协议栈,没有切换,也不需要数据从用户态拷贝到核心态,因为数据就在内核里面。
由此比较,sendfile远比read和write方式在进行数据拷贝时高效。

关于sendfile的应用代码–代码作用是把a文件(和客户端程序在同一目录下)传递给服务器端
服务端

#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
int main(int argc,char* argv[])
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd!=-1);

    struct sockaddr_in saddr,caddr;

    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6500);
    saddr.sin_addr.s_addr = inet_addr("192.168.1.11");

    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    assert(res!=-1);

    listen(sockfd,5);

    int len = sizeof(caddr);
    int c = accept(sockfd,(struct sockaddr*)&caddr,&len);

    while(1)
    {
         if(c<0)
         {
             continue;
         }
         char buff[128] = {
  
  0};
         recv(c,buff,127,0);
         printf("%s",buff);
    }
    close(c);
    return 0;
}

客户端

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

int main(int argc,char* argv[])
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd!=-1);

    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6500);
    saddr.sin_addr.s_addr = inet_addr("192.168.1.11");

    int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    assert(res!=-1);

    int fd1 = open("./a",O_RDONLY);
    int len = 1;
    while(len)
    {
        len = sendfile(sockfd,fd1,0,1024);
        if(len==0)
        {
            break;
        }
        printf("发出了%d个字节\n",len);
    }
    close(sockfd);
    return 0;

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Nginx 配置 HTTPS 完整过程[通俗易懂]

    Nginx 配置 HTTPS 完整过程[通俗易懂]配置站点使用https,并且将http重定向至https。1.nginx的ssl模块安装查看nginx是否安装http_ssl_module模块。$/usr/local/nginx/sbin/nginx-V![在这里插入图片描述](https://img-blog.csdnimg.cn/2018112610054317.png)如果出现configure…

    2022年7月14日
    12
  • 网卡bonding模式 – bond0、1、4配置

    网卡bonding简介网卡绑定就是把多张物理网卡通过软件虚拟成一个虚拟的网卡,配置完毕后,所有的物理网卡的ip和mac将会变成相同的。多网卡同时工作可以提高网络速度,还可以实现网卡的负载均衡、冗余。bonding模式1    round-robin(mode=0)轮转策略,轮流在每一个slave网卡上发送数据包,提供负载均衡和容错能力。     

    2022年4月7日
    34
  • hashmap和hashtable和hashset的区别_反映和反应的区别

    hashmap和hashtable和hashset的区别_反映和反应的区别HashMap与Hashtable的区别是面试中经常遇到的一个问题。这个问题看似简单,但如果深究进去,也能了解到不少知识。本文对两者从来源,特性,算法等多个方面进行对比总结。力争多角度,全方位的展示二者的不同,做到此问题的终结版。作者Hashtable的作者:HashMap的作者:HashMap的作者比Hashtable的作者多了著名顶顶的并发大神DougLea。他写了util…

    2022年9月17日
    0
  • linux rpm 卸载 java_linux下用rpm 安装卸载jdk「建议收藏」

    linux rpm 卸载 java_linux下用rpm 安装卸载jdk「建议收藏」1、如果linux是centos的话,请先卸载openjdkjava-version,会有下面的信息:卸载默认的用root用户登陆到系统,打开一个终端输入#rpm-qa|grepgcj显示内容其中包含下面两行信息#java-1.4.2-gcj-compat-1.4.2.0-27jpp#java-1.4.2-gcj-compat-devel-l.4.2.0-27jpp卸载#rpm-…

    2022年9月15日
    0
  • Android中Calendar类的用法总结[通俗易懂]

    Android中Calendar类的用法总结[通俗易懂]Calendar是Android开发中需要获取时间时必不可少的一个工具类,通过这个类可以获得的时间信息还是很丰富的,下面做一个总结,以后使用的时候就不用总是去翻书或者查资料了。

    2022年9月23日
    0
  • Windows Server 2008 防火墙开放 Oracle 的1521端口

    Windows Server 2008 防火墙开放 Oracle 的1521端口在防火墙的入站规则中,新建端口规则。过程如下例图片所示:同理可以开放EM用的1158端口。执行完后用下面命令测试telnetSERVER_IP1521参考资料[1]WindowsServer2008防火墙如何配置(5).http://www.bitscn.com/netpro/firewall/200711/118934_

    2022年5月9日
    187

发表回复

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

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