sendfile函数--零拷贝
来源:互联网 发布:未来计价软件 编辑:程序博客网 时间:2024/06/10 01:20
零拷贝:零拷贝技术可以减少数据拷贝和共享总线操作的次数,消除通信数据在存储器之间不必要的中间拷贝过程,有效地提高通信效率,是设计高速接口通道、实现高速服务器和路由器的关键技术之一。
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;}
- sendfile函数--零拷贝
- Linux "零拷贝" sendfile函数
- 【Linux编程】零拷贝之sendfile( )函数
- 零拷贝与 sendfile
- sendfile零拷贝
- Linux "零拷贝" sendfile函数中文说明及实际操作分析
- Linux "零拷贝" sendfile函数中文说明及实际操作分析
- Linux "零拷贝" sendfile函数中文说明及实际操作分析
- sendfile函数如何实现零拷贝提高性能
- sendfile:Linux中的"零拷贝"
- sendfile()实现零拷贝详解
- sendfile: Linux中的"零拷贝"
- sendfile:Linux中的"零拷贝"
- sendfile:Linux中的"零拷贝"
- Sendfile Linux 系统中的零拷贝
- linux网络编程八:高效的sendfile,实现零拷贝
- Linux网络编程--sendfile零拷贝高效率发送文件
- 扯淡nginx的sendfile零拷贝的概念
- activiti入门十一(表单的介绍以及formService使用)
- word自带参考文献标注功能—以word2013为例
- 字典树 学习笔记
- 常见系统最佳实践系列
- 第九周切面条
- sendfile函数--零拷贝
- Object-C 字典
- [蓝桥杯练习系统]基础练习 十六进制转八进制
- session共享
- 转 Adaboost算法的另一种解释以及前向算法介绍
- 如何初始化引用类型的成员变量
- Pycharm远程:重启后无法连接远程----解决办法
- JavaScript热修复 oc js 函数对象传参 决解办法
- java调用webservice,比较简单方便的方法。