使用 sendfile() 提升网络文件发送性能

http://hily.me/blog/2011/01/use-sendfile-accelerate-file-sending/

 

偶见一好文,清楚地阐述了什么是零拷贝(Zero Copy)以及 sendfile 的由来,不复述下实感不快。
原文见:
http://www.linuxjournal.com/article/6345

 

文章中列出了我们平时通过网络发送文件时会用到的两个系统调用:
read(file, tmp_buf, len);
write(socket, tmp_buf, len);

调用过程示意图如下:

在用户空间调用 read() 读取文件时发生两次内存拷贝:

  1. DMA引擎将文件读取到内核的文件缓冲区
  2. 调用返回用户空间时将内核的文件缓冲区的数据复制到用户空间的缓冲区

接着调用 write() 把数据写入 socket 时,又发生了两次内存拷贝:

  1. 将用户空间的缓冲区的数据复制到内核的 socket 缓冲区
  2. 将内核 socket 缓冲区的数据复制到网络协议引擎

也就是说,在整个文件发送的过程中,发生了四次内存拷贝。
然后,数据读取到用户空间后并没有做过任何加工处理,因此通过网络发送文件时,根本没有必要把文件内容复制到用户空间。

于是引入了 mmap():
tmp_buf = mmap(file, len);
write(socket, tmp_buf, len);

调用过程示意图:

  1. 调用 mmap() 时会将文件直接读取到内核缓冲区,并把内核缓冲区直接共享到用户空间
  2. 调用 write() 时,直接将内核缓冲区的数据复制到网络协议引擎

这样一来,就少了用户空间和内核空间之间的内存复制了。
这种方式会有个问题,当前进程在调用 write() 时,另一个进程把文件清空了,程序就会报出 SIGBUS 类型错误。

Linux Kernel 2.1 引进了 sendfile(),只需要一个系统调用来实现文件发送。
sendfile(socket, file, len);

调用过程示意图:

  1. 调用 sendfile() 时会直接在内核空间把文件读取到内核的文件缓冲区
  2. 将内核的文件缓冲区的数据复制到内核的 socket 缓冲区中
  3. 将内核的 socket 缓冲区的数据复制到网络协议引擎

从性能上看,这种方式只是少了一个系统调用而已,还是做了3次拷贝操作。

Linux Kernel 2.4 改进了 sendfile(),调用接口没有变化:
sendfile(socket, file, len);

调用过程示意图:

  1. 调用 sendfile() 时会直接在内核空间把文件读取到内核的文件缓冲区
  2. 内核的 socket 缓冲区中保存的是当前要发送的数据在内核的文件缓冲区中的位置和偏移量
  3. DMA gather copy 将内核的文件缓冲区的数据复制到网络协议引擎

这样就只剩下2次拷贝啦。

在许多 http server 中,都引入了 sendfile 的机制,如 nginx、lighttpd 等,它们正是利用 sendfile() 这个特性来实现高性能的文件发送的。

– EOF –

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值