零拷贝传输(zero-copy transfer)——sendfile()

时间:2023-03-08 21:53:06
零拷贝传输(zero-copy transfer)——sendfile()

  做Web服务器时通常需要将文件传送出去,其中一种方法是通过定义一个buffer每次读取文件发送给接收端。大多数服务器会选择sendfile的方式,nginx实现时就是采用这种方式。对于并发搞得服务器性能上能得到优势。

  零拷贝传输(zero-copy transfer)——sendfile()

  对于第一种方式需要进入内核两次,分别是读取磁盘文件和写入socket,对于操作系统而言进入内核的开销是不可以忽略的,而sendfile() 采取了所谓的 “zero-copy transfer” 的方式,数据在内核中的时候,就从cbuffer cache 送入 socket buffer中了。花销仅仅一次system call。为了突出显示这两者的区别,我选择发送一个大文件来体现他们之间的效率的差距:

send() 关键代码:

(发送文件时定义了一个协议:每次读取文件先将大小发送过去,告诉接收端,这次要接受多大的内容)

 for(;;)
{
bzero(buf,sizeof(buf));
size = read(file_fd,buf,sizeof(buf));
if(!size)
break;
send(fd,&size,sizeof(size),);
send(fd,buf,size,);
}

sendfile() 关键代码 :

sendfile(fd,file,&n,len);

结果比较:

1.

$time a.out
file
All done!

real 0m4.667s
user 0m0.148s
sys 0m1.744s

2.

$time a.out
file
All done.

real 0m3.113s
user 0m0.000s
sys 0m0.020s

  仅仅50M的文件就相差1s了,可以看出主要的差距还是内核时间。服务器选择发送文件时还是sendfile比较靠谱一点。

最后sendfile的原型:

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

  其中in_fd必须是可以进行mmap()操作的,这也就是说接收端不可以用sendfile()接受的,即使前两个参数都是fd,容易给人这样的错觉。