Hi~朋友,关注置顶防止错过消息
传统的文件传输有啥缺点?
传统IO的工作方式是,数据读取和写入是从用户空间和内核空间来回复制,内核空间的数据时通过操作系统层面的IO接口从磁盘读取或写入。
通过上图可以看出,在我们执行read和writer之间,一共发生了4次用户态和内核态上下文切换,在高并发的场景下,用户态和内核态上下文切换带来的性能消耗将会极大的降低系统的性能。
除了上下文的切换,在这个过程也一共发生了4次数据拷贝,其中包含两次DMA拷贝,两次CPU拷贝。
可以看到我们在一个主机上如果想把某个文件的内容通过网络发送出去,将会进行4次切换和4次拷贝,但在这4次拷贝的过程中拷贝的都是同一份数据,过多的数据拷贝造成了系统性能的下降。
因此,为了优化文件的传输性能,我们需要减少上下切换和拷贝的次数。
如何实现零拷贝?
mmap + write
read()系统调用会把内核缓冲区的内容拷贝到用户空间,为了减少这一次的拷贝,我们使用mmap()系统调用替换掉read()
mmap函数会直接把内核缓冲区的数据映射到用户空间,这样操作系统内核和用户空间就不需要再进行数据的拷贝。
mmap + write的实现方式需要3次数据拷贝和4次上下文切换
sendfile
sendfile是Linux内核2.1版本中专门发送文件的系统调用函数,函数形式如下:
# include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
sendfile可以替代read()和write()这两个系统调用,因此系统调用从2次变为1次,相应的上下文切换也会变为2次。
其次,sendfile可以直接把内核缓冲区的数据拷贝到Socket缓冲区,因此通过sendfile函数,我们可以将上下文切换减少为2次,数据拷贝3次。
如何实现真正的零拷贝?
上述零拷贝还不是真正的零拷贝,如果网卡支持SG-DMA技术的话,我们可以进一步减少数据拷贝的次数(即减少CPU把内核缓冲区的内容拷贝到Socket缓冲区的过程)。
$ ethtool -k eth0 | grep scatter-gather
上述命令可以查看网卡是否支持SG-DMA技术。
在Linux内核2.4y以后,对于网卡支持SG-DMA技术的情况下,sendfile系统调用的过程也发生了变化:
通过以上技术,我们真正实现了零拷贝,数据拷贝次数发生两次,并且我们全程没有让CPU介入数据拷贝过程,通过DMA技术实现了数据的拷贝。总体来看,零拷贝技术可以把文件性能至少提高一倍以上。
PageCache是什么?
在我们上面一直提到一个内核缓冲区,该内核缓冲区就是PageCache(磁盘高速缓存)。
PageCache的优点?
PageCache的缺点?
所以针对大文件的传输,不应该使用零拷贝技术。
如何解决大文件传输问题?
异步IO + 直接IO。
异步IO主要解决read方法调用时的阻塞问题,通过上图可以看出:
异步IO整个过程没有涉及到PageCache,绕开PageCache的IO也可以成为直接IO。通常对于磁盘来说,异步IO只支持直接IO。
所以在传输大文件时,可以使用异步IO+直接IO无阻塞的读取文件。
直接IO的使用场景?