bruce 2020-05-09
sendfile
现在流行的web 服务器里面都提供 sendfile 选项用来提高服务器性能,那到底 sendfile是什么,怎么影响性能的呢?sendfile实际上是 Linux2.0+以后的推出的一个系统调用,web服务器可以通过调整自身的配置来决定是否利用 sendfile这个系统调用。先来看一下不用 sendfile的传统网络传输过程:
read(file,tmp_buf, len);
write(socket,tmp_buf, len);
硬盘 >> kernel buffer >> user buffer>> kernel socket buffer >>协议栈
一般来说一个网络应用是通过读硬盘数据,然后写数据到socket 来完成网络传输的。上面2行用代码解释了这一点,不过上面2行简单的代码掩盖了底层的很多操作。来看看底层是怎么执行上面2行代码的:
1、系统调用 read()产生一个上下文切换:从 user mode 切换到 kernel mode,然后 DMA 执行拷贝,把文件数据从硬盘读到一个 kernel buffer 里。
2、数据从 kernel buffer拷贝到 user buffer,然后系统调用 read() 返回,这时又产生一个上下文切换:从kernel mode 切换到 user mode。
3、 系统调用write()产生一个上下文切换:从 user mode切换到 kernel mode,然后把步骤2读到 user buffer的数据拷贝到 kernel buffer(数据第2次拷贝到 kernel buffer),不过这次是个不同的 kernel buffer,这个 buffer和 socket相关联。
4、系统调用 write()返回,产生一个上下文切换:从 kernel mode 切换到 user mode(第4次切换了),然后 DMA 从 kernel buffer拷贝数据到协议栈(第4次拷贝了)。
上面4个步骤有4次上下文切换,有4次拷贝,我们发现如果能减少切换次数和拷贝次数将会有效提升性能。在kernel2.0+ 版本中,系统调用 sendfile() 就是用来简化上面步骤提升性能的。sendfile() 不但能减少切换次数而且还能减少拷贝次数。
再来看一下用 sendfile()来进行网络传输的过程:
sendfile(socket,file, len);
硬盘 >> kernel buffer (快速拷贝到kernelsocket buffer) >>协议栈
1、 系统调用sendfile()通过 DMA把硬盘数据拷贝到 kernel buffer,然后数据被 kernel直接拷贝到另外一个与 socket相关的 kernel buffer。这里没有 user mode和 kernel mode之间的切换,在 kernel中直接完成了从一个 buffer到另一个 buffer的拷贝。
2、DMA 把数据从 kernelbuffer 直接拷贝给协议栈,没有切换,也不需要数据从 user mode 拷贝到 kernel mode,因为数据就在 kernel 里。