关于零拷贝,你应该知道的那些事

最近,学妹在开发一套远程升级工具,须要将本地的软件包传输到远程机器上,然而她发现文件传输比较慢。因而,我想到了零拷贝技术,遂写了此文也算对零拷贝作个详细了解。缓存


问题:如今有一个用户须要读取磁盘文件上的内容而后将其经过网络发送出去,假设使用IO系统调用read/write网络


一、IO原理app

     要了解什么是零拷贝,首先得知道什么是IO,以及有哪些类型的IO。通常而言,IO分为:标准IO库、IO系统调用、网络IO库。socket

IO系统调用ide

     Linux标准访问文件方式是经过两个系统调用实现的:read()和write(),这两个系统调用在用户态都是没有缓冲。当用户进程使用read 和 write 读写Linux的文件时,进程会从用户态进入内核态,经过I/O操做读取文件中的数据。函数

//read()会把参数fd所指的文件传送count 个字节到buf 指针所指的内存中。 ssize_t read(int fd, void * buf, size_t count);//write()会把参数buf所指的内存写入count个字节到参数放到所指的文件内。 ssize_t write (int fd, const void * buf, size_t count);

标准IO库
工具

       标准IO库是基于IO系统调用实现的,优化了对系统调用的使用方式。引入标准IO库主要是对IO系统调用进行封装。并且,read 和 write 等底层系统调用须要在用户态和内核态之间切换,若是每次读写的数据不多,那么切换带来的开销将大大下降IO的效率,因此标准IO库在用户态也引入了缓冲机制,提高了性能。性能

       常见的标准IO库函数:优化

        fopen、fclose、fwrite、fread、ffulsh、fseek等等。spa

图片

      假如按照上面IO系统调用方式,要写入数据到文件上时,内核先将数据写入到内核中所设的缓冲当中;假如这个缓冲储存器的长度是100字节,调用系统函数write时,假设每次要写入的数据的长度为10个字节,那么要调用10次write函数才能将内核缓冲区写满(内核是由缓冲区的),由此能够看出,上下文切换的次数是不少的。


      若是按照标准IO库调用方式,一次调用能够将数据尽量多的写入内核缓存,而后由内核态将数据复制到用户态缓存由于read 和 write 等底层系统调用须要在用户态内核态之间切换,若是每次读写的数据不多,那么切换带来的开销将大大下降IO的效率,因此标准IO库在用户态也引入了缓冲机制,提高了性能。采用内核缓存能够减小磁盘IO的次数,提高磁盘IO的效率。


      如上图所示,无论采用哪一种方式,从磁盘读取文件,再到网络发送。涉及到4次上下文切换和4次拷贝。若是想继续优化,则须要减小上下文切换次数,也就是要减小系统调用的次数。解决方案就是把 read、write 两次系统调用合并成一次,在内核中完成磁盘与网卡的数据交换,则提升了性能。

零拷贝

      咱们知道,数据的拷贝是须要借助内核实现的,而所谓的零拷贝实际上是指在用户态和内核态没有了数据拷贝工做。以下图所示,减小了拷贝次数,也就减小了用户态和内核态的上下文切换次数。

图片

    固然了,若是网卡支持 SG-DMA(Direct Memory Access)技术,还能够再去除 Socket 缓冲区的拷贝,这样一共只有 2 次内存拷贝。这样效率会更高,以下图所示:在用户态发起一次调用,则内核从磁盘加载数据到内核,而后写入网卡队列。写入成功了,则再内核通知socket结果,而后socket调用返回用户态。

图片

       

那么,咱们再多说两句。在Linux下,使用sendfile实现零拷贝的调用,其经历了两次发展变化。

Linux 2.1内核引入sendfile函数

sendfile经过一次系统调用完成了文件的传送,经过sendfile发送文件只须要一次系统调用,当调用sendfile时数据的拷贝路径以下:

第一次拷贝:将数据从磁盘读取到内核缓冲区中;

第二次拷贝:将数据从内核缓冲区拷贝到socket buffer中;

第三次拷贝:将数据从socket buffer拷贝到网卡设备中发送;

Linux2.4 内核对sendfile作了进一步的改进:

改进后的数据拷贝处理路径以下:

第一次拷贝:将文件从磁盘拷贝到内核缓冲区中,再也不将内核缓冲区的数据拷贝到socket buffer,而是向socket buffer中写入当前要发送的数据在内核缓冲区中的位置和偏移量;

第二次拷贝:根据socket buffer中的位置和偏移量,直接将内核缓冲区的数据copy到网卡设备中;

总结:

       每次IO请求,内核态和用户态的切换开销以及数据的拷贝开销会严重下降性能,因此零拷贝技术能够来省掉用户态和内核态之间多余的数据拷贝,大大提升了应用程序的性能,而且减小了内核态和用户态的上下文的切换。对于零拷贝须要记住如下2点:

  1. 零拷贝能够将读取磁盘文件网络传输的上下文切换的次数从4次下降到2次;数据拷贝次数从4次下降到2次;

  2. 零拷贝是针对内核来讲,数据在内核模式下是无拷贝的,并非指整个过程数据没有拷贝;

相关文章
相关标签/搜索