sendfile函数--零拷贝(转)
零拷贝:零拷贝技术可以减少数据拷贝和共享总线操作的次数,消除通信数据在存储器之间不必要的中间拷贝过程,有效地提高通信效率,是设计高速接口通道、实现高速服务器和路由器的关键技术之一。
sendfile
#include <sys/sendfile.h>
ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count);
1
2
参数特别注意的是: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 >> 协议栈
1
2
3
一般的网络应用是通过读硬盘数据,然后写数据到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
2
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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
客户端
#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系统调用--“零拷贝,高效”
参考:http://blog.csdn.net/caianye/article/details/7576198 如今几乎每个人都听说过Linux中所谓的"零拷贝"特性,然而我经常碰 ...
- 深入剖析Linux IO原理和几种零拷贝机制的实现
深入剖析Linux IO原理和几种零拷贝机制的实现 来源 https://zhuanlan.zhihu.com/p/83398714 零壹技术栈 公众号[零壹技术栈] 前言 零拷贝(Zero ...
- 走进科学之揭开神秘的"零拷贝"
前言 "零拷贝"这三个字,想必大家多多少少都有听过吧,这个技术在各种开源组件中都使用了,比如kafka,rocketmq,netty,nginx等等开源框架都在其中引用了这项技术. ...
- 走进科学之揭开神秘的"零拷贝"!
"零拷贝"这三个字,想必大家多多少少都有听过吧,这个技术在各种开源组件中都使用了,比如kafka,rocketmq,netty,nginx等等开源框架都在其中引用了这项技术 ...
- Linux "零拷贝" sendfile函数中文说明及实际操作
Sendfile函数说明 #include ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count); sendfile ...
- Linux "零拷贝" sendfile函数中文说明及实际操作分析
Sendfile函数说明 #include ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count); sendfile ...
- linux网络编程:splice函数和tee( )函数高效的零拷贝
splice( )函数 在两个文件描述符之间移动数据,同sendfile( )函数一样,也是零拷贝. 函数原型: #include <fcntl.h> ssize_t splice(int ...
- Linux网络编程--sendfile零拷贝高效率发送文件
from http://blog.csdn.net/hnlyyk/article/details/50856268 Linux系统使用man sendfile,查看sendfile原型如下: #inc ...
- linux网络编程九:splice函数,高效的零拷贝
from:http://blog.csdn.net/jasonliuvip/article/details/22600569 linux网络编程九:splice函数,高效的零拷贝 最近在看<Li ...
随机推荐
- Jmeter(二十一)Jmeter常见问题及场景应用
Jmeter作为工具来讲,已经是一个相对比较牛掰的工具,除了它能够支持那么多协议以及方法之外,更在与它的前置处理以及后置处理.同步监控的人性化.当然,所有的工具.框架都是作为业务的支撑,如果不能满足我 ...
- java中synchronized 用在实例方法和对象方法上面的区别
https://bijian1013.iteye.com/blog/1836575 在Java中,synchronized 是用来表示同步的,我们可以synchronized 来修饰一个方法.也可以s ...
- IIS 反向代理设置
http://blog.csdn.net/yuanguozhengjust/article/details/23576033
- Html5——canvas标签使用
canvas 拥有多种绘制路径.矩形.圆形.字符以及添加图像的方法. canvas 元素本身是没有绘图能力的.所有的绘制工作必须在 JavaScript 内部完成 <script type=&q ...
- OpenGL中移动单位中的‘单位’指什么
opengl 比如 用到glm::translate(x,y,z) 表示 移动x,y,z个单位, 那么这个这个单位是指什么呢?这里的单位不是指像素,是根据投影矩阵的变化而变化的,默认情况下投影矩阵Pr ...
- Java - 33 Java Applet基础
Java Applet基础 applet是一种Java程序.它一般运行在支持Java的Web浏览器内.因为它有完整的Java API支持,所以applet是一个全功能的Java应用程序. 如下所示是独 ...
- oracle提高查询效率的34个方面全解析
oracle提高查询效率的34个方面全解析 在一个数据库中进行操作的时候,效率是很重要的,那么,如何提高oracle的查询效率呢?笔者将从以下几个方面进行详细解析: 1.选择最有效率的表名顺序(只 ...
- python学习之----BeautifulSoup的find()和findAll()及四大对象
BeautifulSoup 里的find() 和findAll() 可能是你最常用的两个函数.借助它们,你可以通 过标签的不同属性轻松地过滤HTML 页面,查找需要的标签组或单个标签. 这两个函数非常 ...
- 微信小程序笔记<三>入口app.js —— 注册小程序
小程序开发框架在逻辑层使用的语言就是JavaScript,所以想玩小程序JavaScript的基本功一定要扎实.但小程序基于js做了一些修改,以方便开发者更方便的使用微信的一些功能,使得小程序更好的贴 ...
- ES6入门声明
let.var区别点 1.let只在命令所在的代码块中有效. 2.变量一定要先声明在使用,否则会报错,不存在Es5的变量提升(暂时性死区,不存在重复使用). 3.块级作用域中存在let命令,所声明的变 ...