摘自:http://hily.me/blog/2011/01/use-sendfile-accelerate-file-sending/
英语原文见:http://www.linuxjournal.com/article/6345
文章中列出了我们平时通过网络发送文件时会用到的两个系统调用:
read(file, tmp_buf, len);
write(socket, tmp_buf, len);
调用过程示意图如下:
在用户空间调用 read() 读取文件时发生两次内存拷贝:
接着调用 write() 把数据写入 socket 时,又发生了两次内存拷贝:
也就是说,在整个文件发送的过程中,发生了四次内存拷贝。
然后,数据读取到用户空间后并没有做过任何加工处理,因此通过网络发送文件时,根本没有必要把文件内容复制到用户空间。
于是引入了 mmap():
tmp_buf = mmap(file, len);
write(socket, tmp_buf, len);
调用过程示意图:
这样一来,就少了用户空间和内核空间之间的内存复制了。
这种方式会有个问题,当前进程在调用 write() 时,另一个进程把文件清空了,程序就会报出 SIGBUS 类型错误。
Linux Kernel 2.1 引进了 sendfile(),只需要一个系统调用来实现文件发送。
sendfile(socket, file, len);
调用过程示意图:
从性能上看,这种方式只是少了一个系统调用而已,还是做了3次拷贝操作。
Linux Kernel 2.4 改进了 sendfile(),调用接口没有变化:
sendfile(socket, file, len);
调用过程示意图:
这样就只剩下2次拷贝啦。
在许多 http server 中,都引入了 sendfile 的机制,如 nginx、lighttpd 等,它们正是利用 sendfile() 这个特性来实现高性能的文件发送的。