00:02
IO与零拷贝,我们先对零拷贝做基本的介绍。所谓零拷贝是指网络。编程的关键,很多性能优化都离不开它,也就是说我们要做性能优化的时候,尤其是对文件的传输。过程中呢,我们往往会使用零拷贝这个特性来提升我们的性能。那么在Java程序里面常用的零拷贝主要有两种方式,一种呢叫memory map及内存映射。第二种呢,用的是一个send报一个方法。那么在操作系统里边。内存映射和我们set file,它到底是怎样一个设计,它跟我们的零拷贝又有怎样的关系呢?我们来做一个分析,然后把这两种零拷贝的形式,或者说呃,实现方式说完以后呢,我们来看一下在。
01:01
IO中如何使用零拷贝?好,同学们,下面呢。一些内容都相对比较理论化,所以说同学们听的时候呢,要有这样一个认识啊,就说很多东西呢,可能老师呃,就是用图解的方式跟大家进行一个说明,更多的是一些概念性的东西。同学们看,这是我们一段传统的IO数据读写的代码,同学们应该都会非常熟悉,创建了一个文件。OK,创建了一创建一个文件,然后呢,得到了一个random access file的对象,然后。开辟了或者创建了字节数组。这些数组的大小呢?跟这个文件的大小保持一致,然后read。也就是说我们现在呢,通过raf的read方法,把我们文件的数据读入到我们这一个字节数组。然后呢,有一个server socket,对,拿到一个server socket,通过socket得到output STEM流对象,然后write出去,实际上就是一个文件的读,一个文件的写的过程,对不对,那么我们来分析一下。
02:13
在我们传统的IO数据读写中,我们这里一共发生了多少次,多少次文件拷贝,以及我们这个用户和用户态和内核态的状态的切换,来我们分析一下。首先呢,我们先来看传统IO的它的一个模型图,或者叫IO的读写的一个示意图。同学们看上面这个代表的是。它的状态的切换。这个叫user用户态,User context用户上下文,这是用户态。用户态下面这个呢,是科叫内核态,这是跟我们操作系统有关的哈,就是内核态。
03:02
那么这两个状态,这这个地方又是用户态,这个人又是内核的状态,好我们来看一下刚才这个传统的IO,如果从我们。整个这个模型来看,它是怎么做的呢?首先。他先把硬件就是hard disk,就是我们硬件上的数据进行一个DMA拷贝,那首先我要说一下何为DMA。那我这做,我我把这个DM的概念写到这里哈。D。嗯,这个DNA呢,如果用英文说的话就是direct。下面这个是memory。啊,那个A是ACC,换成中文就是直接内存拷贝。说什么叫直接内存拷贝呢?即不经过CPU。啊,不使用CPU来完成的。
04:01
这个就叫DMA拷贝,那也就是说在我们整个这个过程中,在读的这个过程中呢,比如说我们在前面用了一个read的方法。OK,用了一个read方法过后呢,他首先把硬盘上的数据通过DM拷贝,先拷贝到我们这个内核。八本。然后再把这个内核buffer呢,用CPU拷贝,这个时候这里这地方就用到CPU了,用CPU拷贝到我们的用户buffer。那么我们的数据呢,其实是在用户buffer进行一个修改,修改完毕以后呢,我们再用CPU拷贝到我们的socket buffer也是准备发送的那个socket,它也有8UFF份,然后再用DNA拷贝。直接考虑到我们的协议站,注意。同学们看到的这个图形代表的是协议引擎及协议上这个协议。
05:01
呃,这些呢,如果同学们听得不太清楚,你有个印象就可以了。协议战主要是后面老师会说怎么实现零拷贝,好吧,这是协议站。也就是说整个过程呢,一共经过了几次拷贝,传统的IO经过了四次拷贝。几次切换呢?三次切换。大家有没有发现,在传统的IO这种模型里边,拷贝的次数其实是非常多的。是不是非常多,你看就是一个读一个写,他居然进行了在实际这个操作中进行过了四次,四次拷贝,三次状态的切换,显然代价是非常高的,于是乎呢,就有一种优化的概念,叫做memory map,就叫内存映射优化。所谓内存映射优化,它指的是什么意思呢?是指的将文件映射到内核缓冲区。同时,用户空间可以。
06:02
共享内核空间的数据,这样在进行网络传输时就可以减少内核空间到用户空间的拷贝次数。大家看这张图。还是老规矩,看图哈。我给大家来说一说。在这里呢,因为它使用了,如就说如果我们使用了内存映射技术,那这里呢,同学们看到我们第一次DMN拷贝还是有的,即我们的直接内存拷贝,由我们的硬件拷贝到内核缓冲没有问题,这个是要做的。这时由于。MP就是我们说的内存映射呢,它这个user buffer和buffer可以共享数据,此时这个地方就不会再发生一次CCPU拷贝。好,那么我们的数据呢,就可以直接在内核缓冲进行一个修改,就是说它在底层是在内核缓冲直接进行修改的,修改完了过呢,再通过CPU拷贝到我们socket buffer,然后再通过socket buffer。
07:05
进行DNA拷贝到我们的协议站。同学们有没有发现在MM,也就是说mmm map优化过后呢?我们的拷贝次数减少为三次。但是它的这个状态的切换的次数呢,仍然是三次,没有减少,你看仍然是这有一次切换,这有一次切换,这有一次切换,所以说仍然是三次切换。对,因此呢,MMP其实不是真正的零拷贝,但是呢,它确实少了一次拷贝,因为它可以让我们内核缓冲和我们的UR缓缓冲区共享数据,那于是乎呢,又提出新的概念,就是我们send范的优化。那3FAIR是一个什么呢?是Linux2.1这个版本,它提供了send fair函数,这个函数的底层的原理是数据根本不经过用户态,直接从内核缓冲区进入到socket buffer,同时由于呃,由于和用户态完全无关,还减少了一次上下文的切换,就是我们的状态的切换。
08:12
那同学们看一下,我们仍然分析一下这个图,第一次呢,还是从这个硬件hard hard,呃,Driver里面把数据放到我们的color,进行了一次DMA拷贝,进行一次D拷贝过后呢,同学们注意看到此时此刻这个地方的状态发生的少了一时间切华,你看这是一次,这是第二次,但是同学们看。这个Linuxx2.1这个版本提供的3FAIR尔函数呢,它里面还是经过了一次CPU拷贝,然后有k buffer拷贝到socket buffer,再有socket buffer通过DNA拷贝到我们的协议上,所以说它比前面内存映射这种优化好在什么地方呢?它有四次拷贝减少三次。
09:03
同时呢,由我们原先的这个三次切换,就是上下文的切换,或者说我们状态的切换变成了两次。对,所以说呢,它还是呃有一些优化效果的,但是这种就是Linuxx2.1这个3FAIR呢,仍然没有实现真正的零拷贝,为什么。他这里面还拷贝了一次CPU,我们再说一下,所以零拷贝不是说不拷贝,再听清楚啊,所以零拷贝不是指不拷贝,而是没有CPU拷贝,这个DMA拷贝是没有办法避免的,你不可能说你去对一个数据的传输,你不从这个硬件读到我们k buffer,这是不可能的。所以说我们所谓的零拷贝指的是没有CPU拷贝,我再说一遍。我把它写到这儿。啊,提示大家一下提示。零拷贝,零拷贝是指是从从这个操作系统。
10:06
操作系统角度看的。角度。角度看。啊是没有什么呢,是没有这个CPU拷贝。CPU拷贝是这个意思,但是大家看到我们Linux2.1提供三份二呢,其实没有真正实现零拷贝,它只是由原先的四次拷贝变成了三次,其中还有一次CPU拷贝由三次切换变成了两次切换,进了一步,我们紧接着看下一个在Linux2.4这个版本呢,它内核它做了修改,避免了从内核缓冲区拷贝到烧buffer,诶这个有点意思了,大家注意看一下,这个就实现了真正的离拷贝了,大家看第一次拷贝。还是进行DMA拷贝,就是我们说的啊,直接内存拷贝到color buffer,到了k buffer过后,大家可以看到这个socket buffer呢,它变成一个灰色,指的是什么意思呢?就是。
11:05
K buffer呢,这个地方的数据可以直接进行DM拷贝到我们的协议站了。那为什么这里还画了一个socket buffer?嗯,这个组件组件呢,是因为color buffer的,还是有一些数据要拷贝到socket buffer里面,只是一些很少的数据,比如说这个color buffer的,呃,数据的长度,呃,长度或者是outside,就是那个偏移量,我这都说一下。我这总结一下啊,这里。这里有还其实还是有,其实还是存在,有一次还是有一次CPU拷贝的。CPU拷贝从哪里呢?就从这个坑那。K。啊,八分。到哪里呢?到我们这个so的buffer,但是啊,我说一下,但是但是数据量很少,但是拷贝的信息很少,比如什么呢?比如这一个,呃,Color buffer的N长度,或者是off。
12:15
啊,信息量很少消耗很低消耗。我们说消耗。消耗低。消耗低,可以忽略,可以忽略。忽略。好,所以说其实我们就可以直接认为这次拷贝就没有了,你看他这写的是copy,呃,Description就是拷贝的一些描述信息,其实就是就是一些描述信息,因此呢,这一个拷贝的动作其实就可以忽略掉不记了。那从而就由原先的四次拷贝变成了两次拷贝,哪两次呢?就是从hard的Dis到科buffer,我们的数据在这里进行一个修改或者是一个操作,然后直接通过DM拷贝到我们的协议上。
13:03
完事,注意这些东西呢,虽然有点理论,但是老师也没办法讲,呃,太多东西,因为这里面它涉及到是。这个内核的东西你也看不到。呃,而且他跟操作系统是有关系的,因此呢,同学们只能从理论上来进行一个理解,好吧,小型面试官问到这个,我觉得这几个图已经画的很清晰了,每一次拷贝。到在哪个位置,我们都说的很清楚,说到这个,呃,这个sand fire完了过后呢,其实他就由原先的什么呀,原先的四,最早的四次拷贝变成了两次拷贝,因为有一次这个corner buffer door sock power可以忽略不计,由原先的四次切换啊,三次上下文的切换变成了两次上下文的切换,这就是已经就是一个零拷贝了。这已经是个零拷贝了。好,同学们,那么现在呢,我们再对零拷贝再做一次理解哈。什么叫零拷贝呢?
14:01
所以零拷贝就是从我们操作系统的角度来看,呃,因为内核缓冲区之间没有数据是重复的,我们就认为是零拷贝了啊,你你看刚才我们这个过程中在这里是不是在科B里面只有一份了。啊,你你上面为什么这个有算一次啊,因为你这个呃同第这个2.1的时候,这个corner buffer和socket buffer,其实这两份数据是相同的,对吧,你你你你把这个做在car修改完了,你还拷贝了一份到socket buffer,在这个相当于说在我们整个这个过程中有两份相同数据,这就重复了,所以他不是零拷贝,而这个才是所谓的零拷贝,因为它这里面呢,只有color buffer,而烧buffer已经基本上就是可以忽略不计了,它拷了一些呃描述信息,所以说我们认为什么呢?我们认为在整个过程中,在我们这个呃运行过程中,真正呃在我们。这个内存里边只有一份,可呃,一份八份,而且不是重复的,好,这就是零拷贝,好了,同学们,那么还有一点,零拷贝不仅带来了更少的数据复制,显然传统的IO它其实有两次CPU拷贝到了,我们零拷贝呢,其实一次都没有了啊基,虽然有一点数据拷贝可以忽略,可以忽略不计,所以说零次零拷贝呢,不但带上了更少的数据复制,还带了其他性能优势,例如更少的上下文切换,更少的CPU缓存,缓存这个伪共享以及无CPU校验和计算性能就会大大提升。实际上零拷贝是我们在进行网络数据传输的一个非常重要的优化手段。
15:40
OK,好了,那最后呢,我们再简单整理一下ma,呃,就是我们所说的内存映射和set fire的区别。好的,这个完全是从理论的角度来说,好吧,然后呢,呃,就是面试官问到的时候,你知道他们区别是什么就可以了。我们所说的内存映射呢,它一般适合小数,小数据量的读写,就是数据量比较少的情况下用,3FIRE呢适合大文件的传输。
16:08
我们看到mip就是内存映射,需要四次上下文切换啊,三次数据拷贝。对不对,四次上下文切换,三次数据拷贝,三的呢需要三次上下文的切换,最少两次数据拷贝。两次就说这个3FIRE呢,至少有两次数据拷贝。好,这个地方我这总结的上下文切换跟我说的有点出入哈,我看一下。那就看怎么去理解这个东西了,怎么去理解这个东西了,因为我认为是这算是一次切换,比如说你进来过后,先是一个用户态切换到corner算一次,再用color切换到我们的这个user content预算一次,然后有user呢,再切换到这算一次,就三次,如果你从从最初状态就开始看。
17:00
如果说你从这个地方进入啊,最早的时候先进到一个用户态啊,用用户的上下文,再进入到这个color,再进入到userr,再进入到这个color,那就算四次好吧,这个呢,呃,大家知道就可以了,并不是一个很宽,就是就看你怎么去算,如果你只算中间,只中间几次切换,就跟我刚才说的一样,如果你要把这个头也算上,那就多一次好吧,这个没没什么太大的问题,根据你的理解来说就可以了。啊,不是一个事就就灵活一点好的,那那么这个地方是没有没有出入的,就是MMP呢,它是四次拷贝,因为真正我们说零拷贝,其实最重要的就是数据的拷贝,MIP13次,而三的fire呢,它经过在那个Linux2.4的时候,其实它只有两次拷贝,这个就是一个零拷贝了。好的,三八可以利用DMA减少CP,拷贝mm m mapp是不可以的啊,必须从内核拷贝到sock的缓存,你们可以看到在内存映射的时候,内核就是corner buffer,它的数据要考到socket socket buffer,对吧?好,同学们,我们说了这么多理论的东西,那怎么办呢?我们下面就来给大家讲一个在我们NIO里面怎么去实现您拷贝的案例啊,这个案例呢,我们在下节课为大家进行讲解。
我来说两句