理解了零拷贝原理,总结一下
Posted 叁滴水
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解了零拷贝原理,总结一下相关的知识,希望对你有一定的参考价值。
为什么要零拷贝?
传统的IO拷贝在计算机中拷贝次数太多,速度太慢,零拷贝可以减少拷贝次数,增加系统性能。另外,零拷贝并不是指没有进行文件的拷贝,只是减少了拷贝的次数。
1、基本概念
1.1、 DMA控制器
首先需要知道计算机硬件的读写速度,大概如下:
- CPU高速缓存属于速度最快的,这里可以认为是飞机的速度。
- 网卡的速度,可以认为是汽车速度。
- 硬盘可以认为是走路的速度。
那假如把硬盘的文件通过网卡发送出去,普通IO,CPU高速缓存要向网卡写数据,即使CPU的速度再快,也会被其他两个设备影响。因此CPU需要一个小弟DMA控制器。
直接内存访问(DMA)是一种完全由硬件执行I/O交换的工作方式。在这种方式中,DMA控制器从CPU完全接管对总线的控制,数据交换不经过CPU,而直接在内存和I/O设备之间进行 。DMA方式一般用于高速传送成组数据。DMA控制器将向内存发出地址和控制信号,修改地址,对传送的字的个数计数,并且以中断方式向CPU报告传送操作的结束。
DMA方式的主要优点是速度快。由于CPU根本不参加传送操作,因此就省去了CPU取指令、取数、送数等操作。在数据传送过程中,没有保存现场、恢复现场之类的工作。内存地址修改、传送字个数的计数等等,也不是由软件实现,而是用硬件线路直接实现的。所以DMA方式能满足高速I/O设备的要求,也有利于CPU效率的发挥。
1.2、 CPU用户态和内核态
世界上的人本来就是不平等的,这就好比,你老婆能管你的钱,但是你只能闷头挣钱一样。程序也是如此,有的程序权限很高,可以访问计算机的任何资源,但是有的程序权限就低,只能访问部分资源。这两个类型的程序,就可以映射为CPU的用户态和内核态。方便记忆可以这么理解,内核态是计算机的核心,可以访问计算机的任何资源,如网卡、硬盘。但是为了安全,CPU不能让用户程序肆无忌惮的访问计算机的任何资源,这样如果用户程序不稳定可能会造成系统崩溃,因此才有的用户态。
- 内核态:cpu可以访问内存的所有数据,包括外围设备,例如硬盘,网卡,cpu也可以将自己从一个程序切换到另一个程序。
- 用户态:只能受限的访问内存,且不允许访问外围设备,占用cpu的能力被剥夺,cpu资源可以被其他程序获取。
因此,在CPU想要读取硬盘文件的时候,需要从用户态切换为内核态,才有权限。读取完成之后,为了程序安全,需要从内核态切换为用户态。
2、零拷贝
2.1、普通拷贝
在普通的拷贝时,大概流程如下
- cpu切换到内核态,先到内核态查询内核缓冲区,如果内核缓冲区有,则可以直接拷贝到用户空间中。
- 如果内核缓冲区没有,则CPU会让DMA加载到内核空间中。这里就会有一次DMA拷贝。
- 拷贝到内核缓冲区之后,CPU将会从内核缓冲区拷贝走。这是一次CPU拷贝。拷贝完成切换到用户态。
- 写的时候,再次切换到内核态。切换完成之后,写到socket缓冲区。写完之后,切换到用户态。
- DMA通过异步的方式将socket缓冲区的数据通过网卡发送到对端。
总结这种普通的IO。总共有4次CPU切换(上图蓝色)分别是读2次、写2次。4次文件拷贝,分别是:
- 文件从硬盘到内核空间
- 内核空间到CPU
- CPU到socket缓冲区
- socket缓冲区到网卡。
2.2、mmap零拷贝
mmap是零拷贝的一种方式通过虚拟内存的方式实现。也就是说用户空间和内核空间使用同一个物理地址。这样,文件就不在需要经过用户空间。可以从内核缓冲区直接复制到socket缓冲区。减少了一次文件拷贝。
2.3、sendfile零拷贝
sendfile函数在两个文件描述符之间传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,被称为零拷贝
- 系统调用 sendfile() 通过 DMA 把硬盘数据拷贝到内核缓冲区,然后数据被内核直接拷贝到另外一个与 socket 相关的 socket缓冲区。这里没有 用户态和核心态 之间的切换,在内核中直接完成了从一个 缓冲区 到另一个缓冲区的拷贝。
- DMA 把数据从内核缓冲区 直接拷贝给协议栈,没有切换,也不需要数据从用户态和核心态,因为数据就在 内核里。
总结
零拷贝并不是没有拷贝,是指减少拷贝的次数。有两种方式mmap和sendfile。
- mmap 适合小数据量读写,sendFile 适合大文件传输。(这个并没有查到详细理论依据,如果您有线索,欢迎留言)
- mmap 需要 4 次上下文切换,3 次数据拷贝;sendFile 需要 3 次上下文切换,最少 2 次数据拷贝。
- sendFile 可以利用 DMA 方式,减少 CPU 拷贝,mmap 则不能(必须从内核拷贝到 Socket 缓冲区)。
如果有误,欢迎指正。
以上是关于理解了零拷贝原理,总结一下的主要内容,如果未能解决你的问题,请参考以下文章