sendfile实现零拷贝

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

ssize_t  sendfile(int out_fd , int in_fd ,off_t* offset ,size_t count );

out_fd 是待写入内容的文件描述符,它必须是一个socket

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

可以看出这个函数几乎就是专门为在网络上传输文件设置的,有socket,有文件描述符

offset参数指定从读入文件流的哪个位置开始读,如果为空,则使用读入文件流默认的起始位置

count参数指定在out_fd 和in_fd之间传输的字节数

成功返回传输的字节数,失败返回-1并设置errno

与read和write系统调用函数的比较:

read(file, tmp_buf, len);
write(socket, tmp_buf, len);

如果我们使用read和write函数,需要进行如下几个步骤:

步骤一:系统调用read导致了从用户空间到内核空间的上下文切换。DMA模块(直接内存访问,是一种不经过CPU而直接从内存存取数据的数据交换模式。在DMA模式下,CPU只须向DMA控制器下达指令,让DMA控制器来处理数据的传送,数据传送完毕再把信息反馈给CPU)从磁盘中读取文件内容,并将其存储在内核空间的缓冲区内,完成了第1次复制。

步骤二:数据从内核空间缓冲区复制到用户空间缓冲区,之后系统调用read返回,这导致了从内核空间向用户空间的上下文切换。此时,需要的数据已存放在指定的用户空间缓冲区内(参数tmp_buf),程序可以继续下面的操作。

步骤三:系统调用write导致从用户空间到内核空间的上下文切换。数据从用户空间缓冲区被再次复制到内核空间缓冲区,完成了第3次复制。不过,这次数据存放在内核空间中与使用的socket相关的特定缓冲区中,而不是步骤一中的缓冲区。

步骤四:系统调用返回,导致了第4次上下文切换。第4次复制在DMA模块将数据从内核空间缓冲区传递至协议引擎的时候发生,这与我们的代码的执行是独立且异步发生的。

而使用sendfile函数,需要进行如下几个步骤:

步骤一:sendfile系统调用导致文件内容通过DMA模块被复制到某个内核缓冲区,之后再被复制到与socket相关联的缓冲区内。

步骤二:当DMA模块将位于socket相关联缓冲区中的数据传递给协议引擎时,执行第3次复制。

对比我们可以得出,相比使用sendfile函数,Read & Write方式带来的性能损耗主要有两点:
1. 不必要的内存拷贝。即多了一次从内核缓冲区到用户缓冲区的数据拷贝。
2. 系统调用带来的额外的用户态/内核态上下文切换。我们知道,使用系统调用会发生从内核态到用户态之间的相互转换。使用read&write方式,多使用了一次系统调用,就多了一次上下文切换,降低了性能。

  sendfile函数可能不属于真正意义的零拷贝,但是它减少切换次数和拷贝次数,总体来说是一个非常高效的数据拷贝函数,尤其在传输一个非常大的文件时,sendfile函数的高性能体现的更为明显。所以,一般网络传输文件时,应该使用sendfile函数,提高效率。

 

参考:https://blog.csdn.net/eydwyz/article/details/76637947

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值